diff --git a/sm/call-agent.c b/sm/call-agent.c index 5f4236b69..ec8020517 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -1,1445 +1,1488 @@ /* call-agent.c - Divert GPGSM operations to the agent * Copyright (C) 2001, 2002, 2003, 2005, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include "gpgsm.h" #include #include #include "../common/i18n.h" #include "../common/asshelp.h" #include "keydb.h" /* fixme: Move this to import.c */ #include "../common/membuf.h" #include "../common/shareddefs.h" #include "passphrase.h" static assuan_context_t agent_ctx = NULL; struct cipher_parm_s { ctrl_t ctrl; assuan_context_t ctx; const unsigned char *ciphertext; size_t ciphertextlen; }; struct genkey_parm_s { ctrl_t ctrl; assuan_context_t ctx; const unsigned char *sexp; size_t sexplen; }; struct learn_parm_s { int error; ctrl_t ctrl; assuan_context_t ctx; membuf_t *data; }; struct import_key_parm_s { ctrl_t ctrl; assuan_context_t ctx; const void *key; size_t keylen; }; +struct sethash_inq_parm_s +{ + assuan_context_t ctx; + const void *data; + size_t datalen; +}; + struct default_inq_parm_s { ctrl_t ctrl; assuan_context_t ctx; }; /* Print a warning if the server's version number is less than our version number. Returns an error code on a connection problem. */ static gpg_error_t warn_version_mismatch (ctrl_t ctrl, assuan_context_t ctx, const char *servername, int mode) { gpg_error_t err; char *serverversion; const char *myversion = gpgrt_strusage (13); err = get_assuan_server_version (ctx, mode, &serverversion); if (err) log_error (_("error getting version from '%s': %s\n"), servername, gpg_strerror (err)); else if (compare_version_strings (serverversion, myversion) < 0) { char *warn; warn = xtryasprintf (_("server '%s' is older than us (%s < %s)"), servername, serverversion, myversion); if (!warn) err = gpg_error_from_syserror (); else { log_info (_("WARNING: %s\n"), warn); if (!opt.quiet) { log_info (_("Note: Outdated servers may lack important" " security fixes.\n")); log_info (_("Note: Use the command \"%s\" to restart them.\n"), "gpgconf --kill all"); } gpgsm_status2 (ctrl, STATUS_WARNING, "server_version_mismatch 0", warn, NULL); xfree (warn); } } xfree (serverversion); return err; } /* Try to connect to the agent via socket or fork it off and work by pipes. Handle the server's initial greeting */ static int start_agent (ctrl_t ctrl) { int rc; if (agent_ctx) rc = 0; /* fixme: We need a context for each thread or serialize the access to the agent (which is suitable given that the agent is not MT. */ else { rc = start_new_gpg_agent (&agent_ctx, GPG_ERR_SOURCE_DEFAULT, opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, opt.autostart, opt.verbose, DBG_IPC, gpgsm_status2, ctrl); if (!opt.autostart && gpg_err_code (rc) == GPG_ERR_NO_AGENT) { static int shown; if (!shown) { shown = 1; log_info (_("no gpg-agent running in this session\n")); } } else if (!rc && !(rc = warn_version_mismatch (ctrl, agent_ctx, GPG_AGENT_NAME, 0))) { /* Tell the agent that we support Pinentry notifications. No error checking so that it will work also with older agents. */ assuan_transact (agent_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); /* Pass on the pinentry mode. */ if (opt.pinentry_mode) { char *tmp = xasprintf ("OPTION pinentry-mode=%s", str_pinentry_mode (opt.pinentry_mode)); rc = assuan_transact (agent_ctx, tmp, NULL, NULL, NULL, NULL, NULL, NULL); xfree (tmp); if (rc) log_error ("setting pinentry mode '%s' failed: %s\n", str_pinentry_mode (opt.pinentry_mode), gpg_strerror (rc)); } /* Pass on the request origin. */ if (opt.request_origin) { char *tmp = xasprintf ("OPTION pretend-request-origin=%s", str_request_origin (opt.request_origin)); rc = assuan_transact (agent_ctx, tmp, NULL, NULL, NULL, NULL, NULL, NULL); xfree (tmp); if (rc) log_error ("setting request origin '%s' failed: %s\n", str_request_origin (opt.request_origin), gpg_strerror (rc)); } /* In DE_VS mode under Windows we require that the JENT RNG * is active. */ #ifdef HAVE_W32_SYSTEM if (!rc && opt.compliance == CO_DE_VS) { if (assuan_transact (agent_ctx, "GETINFO jent_active", NULL, NULL, NULL, NULL, NULL, NULL)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), GPG_AGENT_NAME, gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "random-compliance", rc); } } #endif /*HAVE_W32_SYSTEM*/ } } if (!ctrl->agent_seen) { ctrl->agent_seen = 1; audit_log_ok (ctrl->audit, AUDIT_AGENT_READY, rc); } return rc; } /* This is the default inquiry callback. It mainly handles the Pinentry notifications. */ static gpg_error_t default_inq_cb (void *opaque, const char *line) { gpg_error_t err = 0; struct default_inq_parm_s *parm = opaque; ctrl_t ctrl = parm->ctrl; if (has_leading_keyword (line, "PINENTRY_LAUNCHED")) { err = gpgsm_proxy_pinentry_notify (ctrl, line); if (err) log_error (_("failed to proxy %s inquiry to client\n"), "PINENTRY_LAUNCHED"); /* We do not pass errors to avoid breaking other code. */ } else if ((has_leading_keyword (line, "PASSPHRASE") || has_leading_keyword (line, "NEW_PASSPHRASE")) && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK && have_static_passphrase ()) { const char *s = get_static_passphrase (); err = assuan_send_data (parm->ctx, s, strlen (s)); } else log_error ("ignoring gpg-agent inquiry '%s'\n", line); return err; } +/* This is the inquiry callback required by the SETHASH command. */ +static gpg_error_t +sethash_inq_cb (void *opaque, const char *line) +{ + gpg_error_t err = 0; + struct sethash_inq_parm_s *parm = opaque; + + if (has_leading_keyword (line, "TBSDATA")) + { + err = assuan_send_data (parm->ctx, parm->data, parm->datalen); + } + else + log_error ("ignoring gpg-agent inquiry '%s'\n", line); + + return err; +} + + /* Call the agent to do a sign operation using the key identified by - the hex string KEYGRIP. */ + * the hex string KEYGRIP. If DIGESTALGO is given (DIGEST,DIGESTLEN) + * gives the to be signed hash created using the given algo. If + * DIGESTALGO is not given (i.e. zero) (DIGEST,DIGESTALGO) give the + * entire data to-be-signed. */ int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen ) { int rc, i; char *p, line[ASSUAN_LINELENGTH]; membuf_t data; size_t len; struct default_inq_parm_s inq_parm; *r_buf = NULL; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; - if (digestlen*2 + 50 > DIM(line)) + if (digestalgo && digestlen*2 + 50 > DIM(line)) return gpg_error (GPG_ERR_GENERAL); rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; snprintf (line, DIM(line), "SIGKEY %s", keygrip); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; } - sprintf (line, "SETHASH %d ", digestalgo); - p = line + strlen (line); - for (i=0; i < digestlen ; i++, p += 2 ) - sprintf (p, "%02X", digest[i]); - rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (!digestalgo) + { + struct sethash_inq_parm_s sethash_inq_parm; + + sethash_inq_parm.ctx = agent_ctx; + sethash_inq_parm.data = digest; + sethash_inq_parm.datalen = digestlen; + rc = assuan_transact (agent_ctx, "SETHASH --inquire", + NULL, NULL, sethash_inq_cb, &sethash_inq_parm, + NULL, NULL); + } + else + { + snprintf (line, sizeof line, "SETHASH %d ", digestalgo); + p = line + strlen (line); + for (i=0; i < digestlen ; i++, p += 2 ) + sprintf (p, "%02X", digest[i]); + rc = assuan_transact (agent_ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + } if (rc) return rc; init_membuf (&data, 1024); rc = assuan_transact (agent_ctx, "PKSIGN", put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } *r_buf = get_membuf (&data, r_buflen); if (!gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)) { xfree (*r_buf); *r_buf = NULL; return gpg_error (GPG_ERR_INV_VALUE); } return *r_buf? 0 : out_of_core (); } /* Call the scdaemon to do a sign operation using the key identified by the hex string KEYID. */ int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen ) { int rc, i, pkalgo; char *p, line[ASSUAN_LINELENGTH]; membuf_t data; size_t len; const char *hashopt; unsigned char *sigbuf; size_t sigbuflen; struct default_inq_parm_s inq_parm; gcry_sexp_t sig; (void)desc; *r_buf = NULL; switch(digestalgo) { case GCRY_MD_SHA1: hashopt = "--hash=sha1"; break; case GCRY_MD_RMD160:hashopt = "--hash=rmd160"; break; case GCRY_MD_MD5: hashopt = "--hash=md5"; break; case GCRY_MD_SHA256:hashopt = "--hash=sha256"; break; case GCRY_MD_SHA512:hashopt = "--hash=sha512"; break; default: return gpg_error (GPG_ERR_DIGEST_ALGO); } rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (digestlen*2 + 50 > DIM(line)) return gpg_error (GPG_ERR_GENERAL); /* Get the key type from the scdaemon. */ snprintf (line, DIM(line), "SCD READKEY %s", keyid); init_membuf (&data, 1024); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, NULL, NULL, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } p = get_membuf (&data, &len); pkalgo = get_pk_algo_from_canon_sexp (p, len); xfree (p); if (!pkalgo) return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); p = stpcpy (line, "SCD SETDATA " ); for (i=0; i < digestlen ; i++, p += 2 ) sprintf (p, "%02X", digest[i]); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; init_membuf (&data, 1024); snprintf (line, DIM(line), "SCD PKSIGN %s %s", hashopt, keyid); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } sigbuf = get_membuf (&data, &sigbuflen); switch(pkalgo) { case GCRY_PK_RSA: rc = gcry_sexp_build (&sig, NULL, "(sig-val(rsa(s%b)))", (int)sigbuflen, sigbuf); break; case GCRY_PK_ECC: rc = gcry_sexp_build (&sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))", (int)sigbuflen/2, sigbuf, (int)sigbuflen/2, sigbuf + sigbuflen/2); break; case GCRY_PK_EDDSA: rc = gcry_sexp_build (&sig, NULL, "(sig-val(eddsa(r%b)(s%b)))", (int)sigbuflen/2, sigbuf, (int)sigbuflen/2, sigbuf + sigbuflen/2); break; default: rc = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); break; } xfree (sigbuf); if (rc) return rc; rc = make_canon_sexp (sig, r_buf, r_buflen); gcry_sexp_release (sig); if (rc) return rc; assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); return 0; } /* Handle a CIPHERTEXT inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ static gpg_error_t inq_ciphertext_cb (void *opaque, const char *line) { struct cipher_parm_s *parm = opaque; int rc; if (has_leading_keyword (line, "CIPHERTEXT")) { assuan_begin_confidential (parm->ctx); rc = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen); assuan_end_confidential (parm->ctx); } else { struct default_inq_parm_s inq_parm = { parm->ctrl, parm->ctx }; rc = default_inq_cb (&inq_parm, line); } return rc; } /* Call the agent to do a decrypt operation using the key identified by the hex string KEYGRIP. */ int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, ksba_const_sexp_t ciphertext, char **r_buf, size_t *r_buflen ) { int rc; char line[ASSUAN_LINELENGTH]; membuf_t data; struct cipher_parm_s cipher_parm; size_t n, len; char *p, *buf, *endp; size_t ciphertextlen; if (!keygrip || strlen(keygrip) != 40 || !ciphertext || !r_buf || !r_buflen) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; ciphertextlen = gcry_sexp_canon_len (ciphertext, 0, NULL, NULL); if (!ciphertextlen) return gpg_error (GPG_ERR_INV_VALUE); rc = start_agent (ctrl); if (rc) return rc; rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; assert ( DIM(line) >= 50 ); snprintf (line, DIM(line), "SETKEY %s", keygrip); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; } init_membuf (&data, 1024); cipher_parm.ctrl = ctrl; cipher_parm.ctx = agent_ctx; cipher_parm.ciphertext = ciphertext; cipher_parm.ciphertextlen = ciphertextlen; rc = assuan_transact (agent_ctx, "PKDECRYPT", put_membuf_cb, &data, inq_ciphertext_cb, &cipher_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } /* Make sure it is 0 terminated so we can invoke strtoul safely. */ put_membuf (&data, "", 1); buf = get_membuf (&data, &len); if (!buf) return gpg_error (GPG_ERR_ENOMEM); log_assert (len); /* (we forced Nul termination.) */ if (*buf == '(') { if (len < 13 || memcmp (buf, "(5:value", 8) ) /* "(5:valueN:D)\0" */ return gpg_error (GPG_ERR_INV_SEXP); /* Trim any spurious trailing Nuls: */ while (buf[len-1] == 0) len--; if (buf[len-1] != ')') return gpg_error (GPG_ERR_INV_SEXP); len--; /* Drop the final close-paren: */ p = buf + 8; /* Skip leading parenthesis and the value tag. */ len -= 8; /* Count only the data of the second part. */ } else { /* For compatibility with older gpg-agents handle the old style incomplete S-exps. */ len--; /* Do not count the Nul. */ p = buf; } n = strtoul (p, &endp, 10); if (!n || *endp != ':') return gpg_error (GPG_ERR_INV_SEXP); endp++; if (endp-p+n != len) return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */ memmove (buf, endp, n); *r_buflen = n; *r_buf = buf; return 0; } /* Handle a KEYPARMS inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ static gpg_error_t inq_genkey_parms (void *opaque, const char *line) { struct genkey_parm_s *parm = opaque; int rc; if (has_leading_keyword (line, "KEYPARAM")) { rc = assuan_send_data (parm->ctx, parm->sexp, parm->sexplen); } else { struct default_inq_parm_s inq_parm = { parm->ctrl, parm->ctx }; rc = default_inq_cb (&inq_parm, line); } return rc; } /* Call the agent to generate a newkey */ int gpgsm_agent_genkey (ctrl_t ctrl, ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey) { int rc; struct genkey_parm_s gk_parm; membuf_t data; size_t len; unsigned char *buf; *r_pubkey = NULL; rc = start_agent (ctrl); if (rc) return rc; rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; init_membuf (&data, 1024); gk_parm.ctrl = ctrl; gk_parm.ctx = agent_ctx; gk_parm.sexp = keyparms; gk_parm.sexplen = gcry_sexp_canon_len (keyparms, 0, NULL, NULL); if (!gk_parm.sexplen) return gpg_error (GPG_ERR_INV_VALUE); rc = assuan_transact (agent_ctx, "GENKEY", put_membuf_cb, &data, inq_genkey_parms, &gk_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } buf = get_membuf (&data, &len); if (!buf) return gpg_error (GPG_ERR_ENOMEM); if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } *r_pubkey = buf; return 0; } /* Call the agent to read the public key part for a given keygrip. If FROMCARD is true, the key is directly read from the current smartcard. In this case HEXKEYGRIP should be the keyID (e.g. OPENPGP.3). */ int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, ksba_sexp_t *r_pubkey) { int rc; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; *r_pubkey = NULL; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; snprintf (line, DIM(line), "%sREADKEY %s", fromcard? "SCD ":"", hexkeygrip); init_membuf (&data, 1024); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } buf = get_membuf (&data, &len); if (!buf) return gpg_error (GPG_ERR_ENOMEM); if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } *r_pubkey = buf; return 0; } /* Take the serial number from LINE and return it verbatim in a newly allocated string. We make sure that only hex characters are returned. */ static char * store_serialno (const char *line) { const char *s; char *p; for (s=line; hexdigitp (s); s++) ; p = xtrymalloc (s + 1 - line); if (p) { memcpy (p, line, s-line); p[s-line] = 0; } return p; } /* Callback for the gpgsm_agent_serialno function. */ static gpg_error_t scd_serialno_status_cb (void *opaque, const char *line) { char **r_serialno = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { xfree (*r_serialno); *r_serialno = store_serialno (line); } return 0; } /* Call the agent to read the serial number of the current card. */ int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno) { int rc; char *serialno = NULL; struct default_inq_parm_s inq_parm; *r_serialno = NULL; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, "SCD SERIALNO", NULL, NULL, default_inq_cb, &inq_parm, scd_serialno_status_cb, &serialno); if (!rc && !serialno) rc = gpg_error (GPG_ERR_INTERNAL); if (rc) { xfree (serialno); return rc; } *r_serialno = serialno; return 0; } /* Callback for the gpgsm_agent_serialno function. */ static gpg_error_t scd_keypairinfo_status_cb (void *opaque, const char *line) { strlist_t *listaddr = opaque; const char *keyword = line; int keywordlen; strlist_t sl; char *p; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { sl = append_to_strlist (listaddr, line); p = sl->d; /* Make sure that we only have two tokens so that future * extensions of the format won't change the format expected by * the caller. */ while (*p && !spacep (p)) p++; if (*p) { while (spacep (p)) p++; while (*p && !spacep (p)) p++; if (*p) { *p++ = 0; while (spacep (p)) p++; while (*p && !spacep (p)) { switch (*p++) { case 'c': sl->flags |= GCRY_PK_USAGE_CERT; break; case 's': sl->flags |= GCRY_PK_USAGE_SIGN; break; case 'e': sl->flags |= GCRY_PK_USAGE_ENCR; break; case 'a': sl->flags |= GCRY_PK_USAGE_AUTH; break; } } } } } return 0; } /* Call the agent to read the keypairinfo lines of the current card. The list is returned as a string made up of the keygrip, a space and the keyid. The flags of the string carry the usage bits. */ int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list) { int rc; strlist_t list = NULL; struct default_inq_parm_s inq_parm; *r_list = NULL; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, "SCD LEARN --keypairinfo", NULL, NULL, default_inq_cb, &inq_parm, scd_keypairinfo_status_cb, &list); if (!rc && !list) rc = gpg_error (GPG_ERR_NO_DATA); if (rc) { free_strlist (list); return rc; } *r_list = list; return 0; } static gpg_error_t istrusted_status_cb (void *opaque, const char *line) { struct rootca_flags_s *flags = opaque; const char *s; if ((s = has_leading_keyword (line, "TRUSTLISTFLAG"))) { line = s; if (has_leading_keyword (line, "relax")) flags->relax = 1; else if (has_leading_keyword (line, "cm")) flags->chain_model = 1; } return 0; } /* Ask the agent whether the certificate is in the list of trusted keys. The certificate is either specified by the CERT object or by the fingerprint HEXFPR. ROOTCA_FLAGS is guaranteed to be cleared on error. */ int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr, struct rootca_flags_s *rootca_flags) { int rc; char line[ASSUAN_LINELENGTH]; memset (rootca_flags, 0, sizeof *rootca_flags); if (cert && hexfpr) return gpg_error (GPG_ERR_INV_ARG); rc = start_agent (ctrl); if (rc) return rc; if (hexfpr) { snprintf (line, DIM(line), "ISTRUSTED %s", hexfpr); } else { char *fpr; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (!fpr) { log_error ("error getting the fingerprint\n"); return gpg_error (GPG_ERR_GENERAL); } snprintf (line, DIM(line), "ISTRUSTED %s", fpr); xfree (fpr); } rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, istrusted_status_cb, rootca_flags); if (!rc) rootca_flags->valid = 1; return rc; } /* Ask the agent to mark CERT as a trusted Root-CA one */ int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert) { int rc; char *fpr, *dn, *dnfmt; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (!fpr) { log_error ("error getting the fingerprint\n"); return gpg_error (GPG_ERR_GENERAL); } dn = ksba_cert_get_issuer (cert, 0); if (!dn) { xfree (fpr); return gpg_error (GPG_ERR_GENERAL); } dnfmt = gpgsm_format_name2 (dn, 0); xfree (dn); if (!dnfmt) return gpg_error_from_syserror (); snprintf (line, DIM(line), "MARKTRUSTED %s S %s", fpr, dnfmt); ksba_free (dnfmt); xfree (fpr); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inq_parm, NULL, NULL); return rc; } /* Ask the agent whether the a corresponding secret key is available for the given keygrip */ int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip) { int rc; char line[ASSUAN_LINELENGTH]; rc = start_agent (ctrl); if (rc) return rc; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); snprintf (line, DIM(line), "HAVEKEY %s", hexkeygrip); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); return rc; } static gpg_error_t learn_status_cb (void *opaque, const char *line) { struct learn_parm_s *parm = opaque; const char *s; /* Pass progress data to the caller. */ if ((s = has_leading_keyword (line, "PROGRESS"))) { line = s; if (parm->ctrl) { if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } return 0; } static gpg_error_t learn_cb (void *opaque, const void *buffer, size_t length) { struct learn_parm_s *parm = opaque; size_t len; char *buf; ksba_cert_t cert; int rc; if (parm->error) return 0; if (buffer) { put_membuf (parm->data, buffer, length); return 0; } /* END encountered - process what we have */ buf = get_membuf (parm->data, &len); if (!buf) { parm->error = gpg_error (GPG_ERR_ENOMEM); return 0; } if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, "learncard C 0 0")) return gpg_error (GPG_ERR_ASS_CANCELED); /* FIXME: this should go into import.c */ rc = ksba_cert_new (&cert); if (rc) { parm->error = rc; return 0; } rc = ksba_cert_init_from_mem (cert, buf, len); if (rc) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc)); ksba_cert_release (cert); parm->error = rc; return 0; } /* We do not store a certifciate with missing issuers as ephemeral because we can assume that the --learn-card command has been used on purpose. */ rc = gpgsm_basic_cert_check (parm->ctrl, cert); if (rc && gpg_err_code (rc) != GPG_ERR_MISSING_CERT && gpg_err_code (rc) != GPG_ERR_MISSING_ISSUER_CERT) log_error ("invalid certificate: %s\n", gpg_strerror (rc)); else { int existed; if (!keydb_store_cert (parm->ctrl, cert, 0, &existed)) { if (opt.verbose > 1 && existed) log_info ("certificate already in DB\n"); else if (opt.verbose && !existed) log_info ("certificate imported\n"); } } ksba_cert_release (cert); init_membuf (parm->data, 4096); return 0; } /* Call the agent to learn about a smartcard */ int gpgsm_agent_learn (ctrl_t ctrl) { int rc; struct learn_parm_s learn_parm; membuf_t data; size_t len; rc = start_agent (ctrl); if (rc) return rc; rc = warn_version_mismatch (ctrl, agent_ctx, SCDAEMON_NAME, 2); if (rc) return rc; init_membuf (&data, 4096); learn_parm.error = 0; learn_parm.ctrl = ctrl; learn_parm.ctx = agent_ctx; learn_parm.data = &data; rc = assuan_transact (agent_ctx, "LEARN --send", learn_cb, &learn_parm, NULL, NULL, learn_status_cb, &learn_parm); xfree (get_membuf (&data, &len)); if (rc) return rc; return learn_parm.error; } /* Ask the agent to change the passphrase of the key identified by HEXKEYGRIP. If DESC is not NULL, display instead of the default description message. */ int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; } snprintf (line, DIM(line), "PASSWD %s", hexkeygrip); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inq_parm, NULL, NULL); return rc; } /* Ask the agent to pop up a confirmation dialog with the text DESC and an okay and cancel button. */ gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; snprintf (line, DIM(line), "GET_CONFIRMATION %s", desc); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inq_parm, NULL, NULL); return rc; } /* Return 0 if the agent is alive. This is useful to make sure that an agent has been started. */ gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl) { int rc; rc = start_agent (ctrl); if (!rc) rc = assuan_transact (agent_ctx, "NOP", NULL, NULL, NULL, NULL, NULL, NULL); return rc; } static gpg_error_t keyinfo_status_cb (void *opaque, const char *line) { char **serialno = opaque; const char *s, *s2; if ((s = has_leading_keyword (line, "KEYINFO")) && !*serialno) { s = strchr (s, ' '); if (s && s[1] == 'T' && s[2] == ' ' && s[3]) { s += 3; s2 = strchr (s, ' '); if ( s2 > s ) { *serialno = xtrymalloc ((s2 - s)+1); if (*serialno) { memcpy (*serialno, s, s2 - s); (*serialno)[s2 - s] = 0; } } } } return 0; } /* Return the serial number for a secret key. If the returned serial number is NULL, the key is not stored on a smartcard. Caller needs to free R_SERIALNO. */ gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *serialno = NULL; *r_serialno = NULL; err = start_agent (ctrl); if (err) return err; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); snprintf (line, DIM(line), "KEYINFO %s", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &serialno); if (!err && serialno) { /* Sanity check for bad characters. */ if (strpbrk (serialno, ":\n\r")) err = GPG_ERR_INV_VALUE; } if (err) xfree (serialno); else *r_serialno = serialno; return err; } /* Ask for the passphrase (this is used for pkcs#12 import/export. On success the caller needs to free the string stored at R_PASSPHRASE. On error NULL will be stored at R_PASSPHRASE and an appropriate error code returned. If REPEAT is true the agent tries to get a new passphrase (i.e. asks the user to confirm it). */ gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, char **r_passphrase) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *arg4 = NULL; membuf_t data; struct default_inq_parm_s inq_parm; *r_passphrase = NULL; err = start_agent (ctrl); if (err) return err; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (desc_msg && *desc_msg && !(arg4 = percent_plus_escape (desc_msg))) return gpg_error_from_syserror (); snprintf (line, DIM(line), "GET_PASSPHRASE --data%s -- X X X %s", repeat? " --repeat=1 --check --qualitybar":"", arg4); xfree (arg4); init_membuf_secure (&data, 64); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); *r_passphrase = get_membuf (&data, NULL); if (!*r_passphrase) err = gpg_error_from_syserror (); } return err; } /* Retrieve a key encryption key from the agent. With FOREXPORT true the key shall be use for export, with false for import. On success the new key is stored at R_KEY and its length at R_KEKLEN. */ gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; *r_kek = NULL; err = start_agent (ctrl); if (err) return err; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; snprintf (line, DIM(line), "KEYWRAP_KEY %s", forexport? "--export":"--import"); init_membuf_secure (&data, 64); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); *r_kek = buf; *r_keklen = len; return 0; } /* Handle the inquiry for an IMPORT_KEY command. */ static gpg_error_t inq_import_key_parms (void *opaque, const char *line) { struct import_key_parm_s *parm = opaque; gpg_error_t err; if (has_leading_keyword (line, "KEYDATA")) { assuan_begin_confidential (parm->ctx); err = assuan_send_data (parm->ctx, parm->key, parm->keylen); assuan_end_confidential (parm->ctx); } else { struct default_inq_parm_s inq_parm = { parm->ctrl, parm->ctx }; err = default_inq_cb (&inq_parm, line); } return err; } /* Call the agent to import a key into the agent. */ gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl, const void *key, size_t keylen) { gpg_error_t err; struct import_key_parm_s parm; err = start_agent (ctrl); if (err) return err; parm.ctrl = ctrl; parm.ctx = agent_ctx; parm.key = key; parm.keylen = keylen; err = assuan_transact (agent_ctx, "IMPORT_KEY", NULL, NULL, inq_import_key_parms, &parm, NULL, NULL); return err; } /* Receive a secret key from the agent. KEYGRIP is the hexified keygrip, DESC a prompt to be displayed with the agent's passphrase question (needs to be plus+percent escaped). On success the key is stored as a canonical S-expression at R_RESULT and R_RESULTLEN. */ gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char **r_result, size_t *r_resultlen) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; *r_result = NULL; err = start_agent (ctrl); if (err) return err; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } snprintf (line, DIM(line), "EXPORT_KEY %s", keygrip); init_membuf_secure (&data, 1024); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); *r_result = buf; *r_resultlen = len; return 0; } diff --git a/sm/certcheck.c b/sm/certcheck.c index 3dcac2ffa..3604ac788 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -1,751 +1,761 @@ /* certcheck.c - check one certificate * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/membuf.h" /* Return the number of bits of the Q parameter from the DSA key KEY. */ static unsigned int get_dsa_qbits (gcry_sexp_t key) { gcry_sexp_t l1, l2; gcry_mpi_t q; unsigned int nbits; l1 = gcry_sexp_find_token (key, "public-key", 0); if (!l1) return 0; /* Does not contain a key object. */ l2 = gcry_sexp_cadr (l1); gcry_sexp_release (l1); l1 = gcry_sexp_find_token (l2, "q", 1); gcry_sexp_release (l2); if (!l1) return 0; /* Invalid object. */ q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l1); if (!q) return 0; /* Missing value. */ nbits = gcry_mpi_get_nbits (q); gcry_mpi_release (q); return nbits; } static int do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits, gcry_sexp_t pkey, gcry_mpi_t *r_val) { int n; size_t nframe; unsigned char *frame; if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECC) { unsigned int qbits; if ( pkalgo == GCRY_PK_ECC ) qbits = gcry_pk_get_nbits (pkey); else qbits = get_dsa_qbits (pkey); if ( (qbits%8) ) { log_error(_("DSA requires the hash length to be a" " multiple of 8 bits\n")); return gpg_error (GPG_ERR_INTERNAL); } /* Don't allow any Q smaller than 160 bits. We don't want someone to issue signatures from a key with a 16-bit Q or something like that, which would look correct but allow trivial forgeries. Yes, I know this rules out using MD5 with DSA. ;) */ if (qbits < 160) { log_error (_("%s key uses an unsafe (%u bit) hash\n"), gcry_pk_algo_name (pkalgo), qbits); return gpg_error (GPG_ERR_INTERNAL); } /* Check if we're too short. Too long is safe as we'll automatically left-truncate. */ nframe = gcry_md_get_algo_dlen (algo); if (nframe < qbits/8) { log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), (unsigned int)nframe*8, gcry_pk_get_nbits (pkey), gcry_pk_algo_name (pkalgo)); /* FIXME: we need to check the requirements for ECDSA. */ if (nframe < 20 || pkalgo == GCRY_PK_DSA ) return gpg_error (GPG_ERR_INTERNAL); } frame = xtrymalloc (nframe); if (!frame) return out_of_core (); memcpy (frame, gcry_md_read (md, algo), nframe); n = nframe; /* Truncate. */ if (n > qbits/8) n = qbits/8; } else { int i; unsigned char asn[100]; size_t asnlen; size_t len; nframe = (nbits+7) / 8; asnlen = DIM(asn); if (!algo || gcry_md_test_algo (algo)) return gpg_error (GPG_ERR_DIGEST_ALGO); if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) { log_error ("no object identifier for algo %d\n", algo); return gpg_error (GPG_ERR_INTERNAL); } len = gcry_md_get_algo_dlen (algo); if ( len + asnlen + 4 > nframe ) { log_error ("can't encode a %d bit MD into a %d bits frame\n", (int)(len*8), (int)nbits); return gpg_error (GPG_ERR_INTERNAL); } /* We encode the MD in this way: * * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) * * PAD consists of FF bytes. */ frame = xtrymalloc (nframe); if (!frame) return out_of_core (); n = 0; frame[n++] = 0; frame[n++] = 1; /* block type */ i = nframe - len - asnlen -3 ; assert ( i > 1 ); memset ( frame+n, 0xff, i ); n += i; frame[n++] = 0; memcpy ( frame+n, asn, asnlen ); n += asnlen; memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len; assert ( n == nframe ); } if (DBG_CRYPTO) { int j; log_debug ("encoded hash:"); for (j=0; j < nframe; j++) log_printf (" %02X", frame[j]); log_printf ("\n"); } gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe); xfree (frame); return 0; } /* Return the public key algorithm id from the S-expression PKEY. FIXME: libgcrypt should provide such a function. Note that this implementation uses the names as used by libksba. */ static int pk_algo_from_sexp (gcry_sexp_t pkey) { gcry_sexp_t l1, l2; const char *name; size_t n; int algo; l1 = gcry_sexp_find_token (pkey, "public-key", 0); if (!l1) return 0; /* Not found. */ l2 = gcry_sexp_cadr (l1); gcry_sexp_release (l1); name = gcry_sexp_nth_data (l2, 0, &n); if (!name) algo = 0; /* Not found. */ else if (n==3 && !memcmp (name, "rsa", 3)) algo = GCRY_PK_RSA; else if (n==3 && !memcmp (name, "dsa", 3)) algo = GCRY_PK_DSA; else if (n==3 && !memcmp (name, "ecc", 3)) algo = GCRY_PK_ECC; else if (n==13 && !memcmp (name, "ambiguous-rsa", 13)) algo = GCRY_PK_RSA; else algo = 0; gcry_sexp_release (l2); return algo; } /* Return the hash algorithm's algo id from its name given in the * non-null termnated string in (buffer,buflen). Returns 0 on failure * or if the algo is not known. */ static int hash_algo_from_buffer (const void *buffer, size_t buflen) { char *string; int algo; string = xtrymalloc (buflen + 1); if (!string) { log_error (_("out of core\n")); return 0; } memcpy (string, buffer, buflen); string[buflen] = 0; algo = gcry_md_map_name (string); if (!algo) log_error ("unknown digest algorithm '%s' used in certificate\n", string); xfree (string); return algo; } /* Return an unsigned integer from the non-null termnated string * (buffer,buflen). Returns 0 on failure. */ static unsigned int uint_from_buffer (const void *buffer, size_t buflen) { char *string; unsigned int val; string = xtrymalloc (buflen + 1); if (!string) { log_error (_("out of core\n")); return 0; } memcpy (string, buffer, buflen); string[buflen] = 0; val = strtoul (string, NULL, 10); xfree (string); return val; } /* Extract the hash algorithm and the salt length from the sigval. */ static gpg_error_t extract_pss_params (gcry_sexp_t s_sig, int *r_algo, unsigned int *r_saltlen) { gpg_error_t err; gcry_buffer_t ioarray[2] = { {0}, {0} }; err = gcry_sexp_extract_param (s_sig, "sig-val", "&'hash-algo''salt-length'", ioarray+0, ioarray+1, NULL); if (err) { log_error ("extracting params from PSS failed: %s\n", gpg_strerror (err)); return err; } *r_algo = hash_algo_from_buffer (ioarray[0].data, ioarray[0].len); *r_saltlen = uint_from_buffer (ioarray[1].data, ioarray[1].len); xfree (ioarray[0].data); xfree (ioarray[1].data); if (*r_saltlen < 20) { log_error ("length of PSS salt too short\n"); gcry_sexp_release (s_sig); return gpg_error (GPG_ERR_DIGEST_ALGO); } if (!*r_algo) { return gpg_error (GPG_ERR_DIGEST_ALGO); } /* PSS has no hash function firewall like PKCS#1 and thus offers * a path for hash algorithm replacement. To avoid this it makes * sense to restrict the allowed hash algorithms and also allow only * matching salt lengths. According to Peter Gutmann: * "Beware of bugs in the above signature scheme; * I have only proved it secure, not implemented it" * - Apologies to Donald Knuth. * https://www.metzdowd.com/pipermail/cryptography/2019-November/035449.html * * Given the set of supported algorithms currently available in * Libgcrypt and the extra hash checks we have in some compliance * modes, it would be hard to trick gpgsm to verify a forged * signature. However, if eventually someone adds the xor256 hash * algorithm (1.3.6.1.4.1.3029.3.2) to Libgcrypt we would be doomed. */ switch (*r_algo) { case GCRY_MD_SHA1: case GCRY_MD_SHA256: case GCRY_MD_SHA384: case GCRY_MD_SHA512: case GCRY_MD_SHA3_256: case GCRY_MD_SHA3_384: case GCRY_MD_SHA3_512: break; default: log_error ("PSS hash algorithm '%s' rejected\n", gcry_md_algo_name (*r_algo)); return gpg_error (GPG_ERR_DIGEST_ALGO); } if (gcry_md_get_algo_dlen (*r_algo) != *r_saltlen) { log_error ("PSS hash algorithm '%s' rejected due to salt length %u\n", gcry_md_algo_name (*r_algo), *r_saltlen); return gpg_error (GPG_ERR_DIGEST_ALGO); } return 0; } /* Check the signature on CERT using the ISSUER-CERT. This function does only test the cryptographic signature and nothing else. It is assumed that the ISSUER_CERT is valid. */ int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) { const char *algoid; gcry_md_hd_t md = NULL; void *certder = NULL; size_t certderlen; int rc, algo; ksba_sexp_t p; size_t n; gcry_sexp_t s_sig, s_data, s_pkey; int use_pss = 0; int use_eddsa = 0; unsigned int saltlen; + /* Note that we map the 4 algos which current Libgcrypt versions are + * not aware of the OID. */ algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert))); if (!algo && algoid && !strcmp (algoid, "1.2.840.113549.1.1.10")) use_pss = 1; else if (algoid && !strcmp (algoid, "1.3.101.112")) use_eddsa = 1; else if (algoid && !strcmp (algoid, "1.3.101.113")) use_eddsa = 2; + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.1")) + algo = GCRY_MD_SHA224; /* ecdsa-with-sha224 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.2")) + algo = GCRY_MD_SHA256; /* ecdsa-with-sha256 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.3")) + algo = GCRY_MD_SHA384; /* ecdsa-with-sha384 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.4")) + algo = GCRY_MD_SHA512; /* ecdsa-with-sha512 */ else if (!algo) { log_error ("unknown digest algorithm '%s' used in certificate\n", algoid? algoid:"?"); if (algoid && ( !strcmp (algoid, "1.2.840.113549.1.1.2") ||!strcmp (algoid, "1.2.840.113549.2.2"))) log_info (_("(this is the MD2 algorithm)\n")); return gpg_error (GPG_ERR_GENERAL); } /* The the signature from the certificate. */ p = ksba_cert_get_sig_val (cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); ksba_free (p); return gpg_error (GPG_ERR_BUG); } rc = gcry_sexp_sscan ( &s_sig, NULL, (char*)p, n); ksba_free (p); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); return rc; } if (DBG_CRYPTO) gcry_log_debugsxp ("sigval", s_sig); if (use_pss) { rc = extract_pss_params (s_sig, &algo, &saltlen); if (rc) { gcry_sexp_release (s_sig); return rc; } } /* Hash the to-be-signed parts of the certificate or but them into a * buffer for the EdDSA algorithms. */ if (use_eddsa) { membuf_t mb; init_membuf (&mb, 2048); rc = ksba_cert_hash (cert, 1, (void (*)(void *, const void*,size_t))put_membuf, &mb); if (rc) { log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc)); xfree (get_membuf (&mb, NULL)); return rc; } certder = get_membuf (&mb, &certderlen); if (!certder) { rc = gpg_error_from_syserror (); log_error ("getting tbsCertificate failed: %s\n", gpg_strerror (rc)); return rc; } } else { rc = gcry_md_open (&md, algo, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); return rc; } if (DBG_HASHING) gcry_md_debug (md, "hash.cert"); rc = ksba_cert_hash (cert, 1, HASH_FNC, md); if (rc) { log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); return rc; } gcry_md_final (md); } /* Get the public key from the certificate. */ p = ksba_cert_get_public_key (issuer_cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); gcry_md_close (md); ksba_free (p); gcry_sexp_release (s_sig); xfree (certder); return gpg_error (GPG_ERR_BUG); } rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n); ksba_free (p); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); gcry_sexp_release (s_sig); xfree (certder); return rc; } if (DBG_CRYPTO) gcry_log_debugsxp ("pubkey:", s_pkey); if (use_pss) { rc = gcry_sexp_build (&s_data, NULL, "(data (flags pss)" "(hash %s %b)" "(salt-length %u))", hash_algo_to_string (algo), (int)gcry_md_get_algo_dlen (algo), gcry_md_read (md, algo), saltlen); if (rc) BUG (); } else if (use_eddsa) { rc = gcry_sexp_build (&s_data, NULL, "(data(flags eddsa)(hash-algo %s)(value %b))", use_eddsa == 1? "sha512":"shake256", (int)certderlen, certder); xfree (certder); certder = NULL; if (rc) { log_error ("building data for eddsa failed: %s\n", gpg_strerror (rc)); gcry_sexp_release (s_sig); return rc; } } else { /* RSA or DSA: Prepare the hash for verification. */ gcry_mpi_t frame; rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey), gcry_pk_get_nbits (s_pkey), s_pkey, &frame); if (rc) { gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_pkey); return rc; } if ( gcry_sexp_build (&s_data, NULL, "%m", frame) ) BUG (); gcry_mpi_release (frame); } if (DBG_CRYPTO) gcry_log_debugsxp ("data:", s_data); /* Verify. */ rc = gcry_pk_verify (s_sig, s_data, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); if (use_eddsa && (gpg_err_code (rc) == GPG_ERR_INTERNAL || gpg_err_code (rc) == GPG_ERR_INV_CURVE)) { /* Let's assume that this is a certificate for an ECDH key * signed using EdDSA. This won't work. We should have located * the public key using subjectKeyIdentifier (SKI) to not run * into this problem. However, we don't do this for self-signed * certificates and we don't have a way to search for arbitrary * keys based on the SKI. Note: The sample certificate from * RFC-8410 uses a SHA-1 hash of the public key for the SKI; so * we are not able to verify it. */ ksba_sexp_t ski; const unsigned char *skider; size_t skiderlen; if (DBG_X509) log_debug ("retrying using the ski\n"); if (!ksba_cert_get_subj_key_id (issuer_cert, NULL, &ski)) { skider = gpgsm_get_serial (ski, &skiderlen); if (!skider) ; else if (skiderlen == (use_eddsa==1? 32:57)) { /* Here we assume that the SKI is actually the public key. */ gcry_sexp_release (s_pkey); rc = gcry_sexp_build (&s_pkey, NULL, "(public-key(ecc(curve%s)(q%b)))", use_eddsa==1? "1.3.101.112":"1.3.101.113", (int)skiderlen, skider); if (rc) log_error ("building pubkey from SKI failed: %s\n", gpg_strerror (rc)); else rc = gcry_pk_verify (s_sig, s_data, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); } else if (skiderlen == 20) { log_printhex (skider, skiderlen, "ski might be the SHA-1:"); } else { if (DBG_X509) log_debug(skider, skiderlen, "ski is:"); } ksba_free (ski); } } gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_data); gcry_sexp_release (s_pkey); return rc; } int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, gcry_md_hd_t md, int mdalgo, int *r_pkalgo) { int rc; ksba_sexp_t p; gcry_sexp_t s_sig, s_hash, s_pkey, l1; size_t n; const char *s; int i; int pkalgo; int use_pss; unsigned int saltlen = 0; if (r_pkalgo) *r_pkalgo = 0; n = gcry_sexp_canon_len (sigval, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); return gpg_error (GPG_ERR_BUG); } rc = gcry_sexp_sscan (&s_sig, NULL, (char*)sigval, n); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); return rc; } /* Check whether rsaPSS is needed. This is indicated in the SIG-VAL * using a flag. Only if we found that flag, we extract the PSS * parameters for SIG-VAL. */ use_pss = 0; l1 = gcry_sexp_find_token (s_sig, "flags", 0); if (l1) { /* Note that the flag parser assumes that the list of flags * contains only strings and in particular not sublist. This is * always the case or current libksba. */ for (i=1; (s = gcry_sexp_nth_data (l1, i, &n)); i++) if (n == 3 && !memcmp (s, "pss", 3)) { use_pss = 1; break; } gcry_sexp_release (l1); if (use_pss) { int algo; rc = extract_pss_params (s_sig, &algo, &saltlen); if (rc) { gcry_sexp_release (s_sig); return rc; } if (algo != mdalgo) { log_error ("PSS hash algo mismatch (%d/%d)\n", mdalgo, algo); gcry_sexp_release (s_sig); return gpg_error (GPG_ERR_DIGEST_ALGO); } } } p = ksba_cert_get_public_key (cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); ksba_free (p); gcry_sexp_release (s_sig); return gpg_error (GPG_ERR_BUG); } if (DBG_CRYPTO) log_printhex (p, n, "public key: "); rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n); ksba_free (p); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); gcry_sexp_release (s_sig); return rc; } pkalgo = pk_algo_from_sexp (s_pkey); if (r_pkalgo) *r_pkalgo = pkalgo; if (use_pss) { rc = gcry_sexp_build (&s_hash, NULL, "(data (flags pss)" "(hash %s %b)" "(salt-length %u))", hash_algo_to_string (mdalgo), (int)gcry_md_get_algo_dlen (mdalgo), gcry_md_read (md, mdalgo), saltlen); if (rc) BUG (); } else { /* RSA or DSA: Prepare the hash for verification. */ gcry_mpi_t frame; rc = do_encode_md (md, mdalgo, pkalgo, gcry_pk_get_nbits (s_pkey), s_pkey, &frame); if (rc) { gcry_sexp_release (s_sig); gcry_sexp_release (s_pkey); return rc; } /* put hash into the S-Exp s_hash */ if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) BUG (); gcry_mpi_release (frame); } rc = gcry_pk_verify (s_sig, s_hash, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); gcry_sexp_release (s_sig); gcry_sexp_release (s_hash); gcry_sexp_release (s_pkey); return rc; } int gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, unsigned char **r_sigval) { int rc; char *grip, *desc; size_t siglen; grip = gpgsm_get_keygrip_hexstring (cert); if (!grip) return gpg_error (GPG_ERR_BAD_CERT); desc = gpgsm_format_keydesc (cert); rc = gpgsm_agent_pksign (ctrl, grip, desc, gcry_md_read(md, mdalgo), gcry_md_get_algo_dlen (mdalgo), mdalgo, r_sigval, &siglen); xfree (desc); xfree (grip); return rc; } diff --git a/sm/certreqgen.c b/sm/certreqgen.c index 3fbab2d50..e54569ac8 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -1,1425 +1,1472 @@ /* certreqgen.c - Generate a key and a certification [request] * Copyright (C) 2002, 2003, 2005, 2007, 2010, * 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* The format of the parameter file is described in the manual under "Unattended Usage". Here is an example: $ cat >foo < #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" +#include "../common/membuf.h" enum para_name { pKEYTYPE, pKEYLENGTH, pKEYGRIP, pKEYCURVE, pKEYUSAGE, pNAMEDN, pNAMEEMAIL, pNAMEDNS, pNAMEURI, pSERIAL, pISSUERDN, pNOTBEFORE, pNOTAFTER, pSIGNINGKEY, pHASHALGO, pAUTHKEYID, pSUBJKEYID, pEXTENSION }; struct para_data_s { struct para_data_s *next; int lnr; enum para_name key; union { unsigned int usage; char value[1]; } u; }; struct reqgen_ctrl_s { int lnr; int dryrun; }; static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; static const char oidstr_subjectKeyIdentifier[] = "2.5.29.14"; static const char oidstr_keyUsage[] = "2.5.29.15"; static const char oidstr_basicConstraints[] = "2.5.29.19"; static const char oidstr_standaloneCertificate[] = "1.3.6.1.4.1.11591.2.2.1"; static int proc_parameters (ctrl_t ctrl, struct para_data_s *para, estream_t out_fp, struct reqgen_ctrl_s *outctrl); static int create_request (ctrl_t ctrl, struct para_data_s *para, const char *carddirect, ksba_const_sexp_t public, ksba_const_sexp_t sigkey, ksba_writer_t writer); static void release_parameter_list (struct para_data_s *r) { struct para_data_s *r2; for (; r ; r = r2) { r2 = r->next; xfree(r); } } static struct para_data_s * get_parameter (struct para_data_s *para, enum para_name key, int seq) { struct para_data_s *r; for (r = para; r ; r = r->next) if ( r->key == key && !seq--) return r; return NULL; } static const char * get_parameter_value (struct para_data_s *para, enum para_name key, int seq) { struct para_data_s *r = get_parameter (para, key, seq); return (r && *r->u.value)? r->u.value : NULL; } static int get_parameter_algo (struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter (para, key, 0); if (!r) return -1; if (digitp (r->u.value)) return atoi( r->u.value ); return gcry_pk_map_name (r->u.value); } /* Parse the usage parameter. Returns 0 on success. Note that we only care about sign and encrypt and don't (yet) allow all the other X.509 usage to be specified; instead we will use a fixed mapping to the X.509 usage flags. */ static int parse_parameter_usage (struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter (para, key, 0); char *p, *pn; unsigned int use; if (!r) return 0; /* none (this is an optional parameter)*/ use = 0; pn = r->u.value; while ( (p = strsep (&pn, " \t,")) ) { if (!*p) ; else if ( !ascii_strcasecmp (p, "sign") ) use |= GCRY_PK_USAGE_SIGN; else if ( !ascii_strcasecmp (p, "encrypt") || !ascii_strcasecmp (p, "encr") ) use |= GCRY_PK_USAGE_ENCR; else if ( !ascii_strcasecmp (p, "cert") ) use |= GCRY_PK_USAGE_CERT; else { log_error ("line %d: invalid usage list\n", r?r->lnr:0); return -1; /* error */ } } r->u.usage = use; return 0; } static unsigned int get_parameter_uint (struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter (para, key, 0); if (!r) return 0; if (r->key == pKEYUSAGE) return r->u.usage; return (unsigned int)strtoul (r->u.value, NULL, 10); } /* Read the certificate generation parameters from FP and generate (all) certificate requests. */ static int read_parameters (ctrl_t ctrl, estream_t fp, estream_t out_fp) { static struct { const char *name; enum para_name key; int allow_dups; } keywords[] = { { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, { "Key-Grip", pKEYGRIP }, { "Key-Curve", pKEYCURVE }, { "Key-Usage", pKEYUSAGE }, { "Name-DN", pNAMEDN }, { "Name-Email", pNAMEEMAIL, 1 }, { "Name-DNS", pNAMEDNS, 1 }, { "Name-URI", pNAMEURI, 1 }, { "Serial", pSERIAL }, { "Issuer-DN", pISSUERDN }, { "Creation-Date", pNOTBEFORE }, { "Not-Before", pNOTBEFORE }, { "Expire-Date", pNOTAFTER }, { "Not-After", pNOTAFTER }, { "Signing-Key", pSIGNINGKEY }, { "Hash-Algo", pHASHALGO }, { "Authority-Key-Id", pAUTHKEYID }, { "Subject-Key-Id", pSUBJKEYID }, { "Extension", pEXTENSION, 1 }, { NULL, 0 } }; char line[1024], *p; const char *err = NULL; struct para_data_s *para, *r; int i, rc = 0, any = 0; struct reqgen_ctrl_s outctrl; memset (&outctrl, 0, sizeof (outctrl)); err = NULL; para = NULL; while (es_fgets (line, DIM(line)-1, fp) ) { char *keyword, *value; outctrl.lnr++; if (*line && line[strlen(line)-1] != '\n') { err = "line too long"; break; } for (p=line; spacep (p); p++) ; if (!*p || *p == '#') continue; keyword = p; if (*keyword == '%') { for (; *p && !ascii_isspace (*p); p++) ; if (*p) *p++ = 0; for (; ascii_isspace (*p); p++) ; value = p; trim_trailing_spaces (value); if (!ascii_strcasecmp (keyword, "%echo")) log_info ("%s\n", value); else if (!ascii_strcasecmp (keyword, "%dry-run")) outctrl.dryrun = 1; else if (!ascii_strcasecmp( keyword, "%commit")) { rc = proc_parameters (ctrl, para, out_fp, &outctrl); if (rc) goto leave; any = 1; release_parameter_list (para); para = NULL; } else log_info ("skipping control '%s' (%s)\n", keyword, value); continue; } if (!(p = strchr (p, ':')) || p == keyword) { err = "missing colon"; break; } if (*p) *p++ = 0; for (; spacep (p); p++) ; if (!*p) { err = "missing argument"; break; } value = p; trim_trailing_spaces (value); for (i=0; (keywords[i].name && ascii_strcasecmp (keywords[i].name, keyword)); i++) ; if (!keywords[i].name) { err = "unknown keyword"; break; } if (keywords[i].key != pKEYTYPE && !para) { err = "parameter block does not start with \"Key-Type\""; break; } if (keywords[i].key == pKEYTYPE && para) { rc = proc_parameters (ctrl, para, out_fp, &outctrl); if (rc) goto leave; any = 1; release_parameter_list (para); para = NULL; } else if (!keywords[i].allow_dups) { for (r = para; r && r->key != keywords[i].key; r = r->next) ; if (r) { err = "duplicate keyword"; break; } } r = xtrycalloc (1, sizeof *r + strlen( value )); if (!r) { err = "out of core"; break; } r->lnr = outctrl.lnr; r->key = keywords[i].key; strcpy (r->u.value, value); r->next = para; para = r; } if (err) { log_error ("line %d: %s\n", outctrl.lnr, err); rc = gpg_error (GPG_ERR_GENERAL); } else if (es_ferror(fp)) { log_error ("line %d: read error: %s\n", outctrl.lnr, strerror(errno) ); rc = gpg_error (GPG_ERR_GENERAL); } else if (para) { rc = proc_parameters (ctrl, para, out_fp, &outctrl); if (rc) goto leave; any = 1; } if (!rc && !any) rc = gpg_error (GPG_ERR_NO_DATA); leave: release_parameter_list (para); return rc; } /* check whether there are invalid characters in the email address S */ static int has_invalid_email_chars (const char *s) { int at_seen=0; static char valid_chars[] = "01234567890_-." "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (; *s; s++) { if (*s & 0x80) return 1; if (*s == '@') at_seen++; else if (!at_seen && !( !!strchr (valid_chars, *s) || *s == '+')) return 1; else if (at_seen && !strchr (valid_chars, *s)) return 1; } return at_seen != 1; } /* Check that all required parameters are given and perform the action */ static int proc_parameters (ctrl_t ctrl, struct para_data_s *para, estream_t out_fp, struct reqgen_ctrl_s *outctrl) { gpg_error_t err; struct para_data_s *r; const char *s, *string; int i; int algo; unsigned int nbits; char numbuf[20]; unsigned char keyparms[100]; int rc = 0; ksba_sexp_t public = NULL; ksba_sexp_t sigkey = NULL; int seq; size_t erroff, errlen; char *cardkeyid = NULL; /* Check that we have all required parameters; */ assert (get_parameter (para, pKEYTYPE, 0)); /* There is a problem with pkcs-10 on how to use ElGamal because it is expected that a PK algorithm can always be used for signing. Another problem is that on-card generated encryption keys may not be used for signing. */ algo = get_parameter_algo (para, pKEYTYPE); if (!algo && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s) { /* Hack to allow creation of certificates directly from a smart card. For example: "Key-Type: card:OPENPGP.3". */ if (!strncmp (s, "card:", 5) && s[5]) cardkeyid = xtrystrdup (s+5); } if (algo < 1 && !cardkeyid) { r = get_parameter (para, pKEYTYPE, 0); if (r) log_error (_("line %d: invalid algorithm\n"), r?r->lnr:0); else log_error ("No Key-Type specified\n"); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check the keylength. NOTE: If you change this make sure that it macthes the gpgconflist item in gpgsm.c */ if (!get_parameter (para, pKEYLENGTH, 0)) nbits = 3072; else nbits = get_parameter_uint (para, pKEYLENGTH); if ((nbits < 1024 || nbits > 4096) && !cardkeyid) { /* The BSI specs dated 2002-11-25 don't allow lengths below 1024. */ r = get_parameter (para, pKEYLENGTH, 0); log_error (_("line %d: invalid key length %u (valid are %d to %d)\n"), r?r->lnr:0, nbits, 1024, 4096); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check the usage. */ if (parse_parameter_usage (para, pKEYUSAGE)) { xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check that there is a subject name and that this DN fits our requirements. */ if (!(s=get_parameter_value (para, pNAMEDN, 0))) { r = get_parameter (para, pNAMEDN, 0); log_error (_("line %d: no subject name given\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } err = ksba_dn_teststr (s, 0, &erroff, &errlen); if (err) { r = get_parameter (para, pNAMEDN, 0); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME) log_error (_("line %d: invalid subject name label '%.*s'\n"), r?r->lnr:0, (int)errlen, s+erroff); else log_error (_("line %d: invalid subject name '%s' at pos %d\n"), r?r->lnr:0, s, (int)erroff); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check that the optional email address is okay. */ for (seq=0; (s=get_parameter_value (para, pNAMEEMAIL, seq)); seq++) { if (has_invalid_email_chars (s) || *s == '@' || s[strlen(s)-1] == '@' || s[strlen(s)-1] == '.' || strstr(s, "..")) { r = get_parameter (para, pNAMEEMAIL, seq); log_error (_("line %d: not a valid email address\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional serial number. */ string = get_parameter_value (para, pSERIAL, 0); if (string) { if (!strcmp (string, "random")) ; /* Okay. */ else { for (s=string, i=0; hexdigitp (s); s++, i++) ; if (*s) { r = get_parameter (para, pSERIAL, 0); log_error (_("line %d: invalid serial number\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } } /* Check the optional issuer DN. */ string = get_parameter_value (para, pISSUERDN, 0); if (string) { err = ksba_dn_teststr (string, 0, &erroff, &errlen); if (err) { r = get_parameter (para, pISSUERDN, 0); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME) log_error (_("line %d: invalid issuer name label '%.*s'\n"), r?r->lnr:0, (int)errlen, string+erroff); else log_error (_("line %d: invalid issuer name '%s' at pos %d\n"), r?r->lnr:0, string, (int)erroff); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional creation date. */ string = get_parameter_value (para, pNOTBEFORE, 0); if (string && !string2isotime (NULL, string)) { r = get_parameter (para, pNOTBEFORE, 0); log_error (_("line %d: invalid date given\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check the optional expire date. */ string = get_parameter_value (para, pNOTAFTER, 0); if (string && !string2isotime (NULL, string)) { r = get_parameter (para, pNOTAFTER, 0); log_error (_("line %d: invalid date given\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Get the optional signing key. */ string = get_parameter_value (para, pSIGNINGKEY, 0); if (string) { rc = gpgsm_agent_readkey (ctrl, 0, string, &sigkey); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: error getting signing key by keygrip '%s'" ": %s\n"), r?r->lnr:0, s, gpg_strerror (rc)); xfree (cardkeyid); return rc; } } /* Check the optional hash-algo. */ { int mdalgo; string = get_parameter_value (para, pHASHALGO, 0); if (string && !((mdalgo = gcry_md_map_name (string)) && (mdalgo == GCRY_MD_SHA1 || mdalgo == GCRY_MD_SHA256 || mdalgo == GCRY_MD_SHA384 || mdalgo == GCRY_MD_SHA512))) { r = get_parameter (para, pHASHALGO, 0); log_error (_("line %d: invalid hash algorithm given\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional AuthorityKeyId. */ string = get_parameter_value (para, pAUTHKEYID, 0); if (string) { for (s=string, i=0; hexdigitp (s); s++, i++) ; if (*s || (i&1)) { r = get_parameter (para, pAUTHKEYID, 0); log_error (_("line %d: invalid authority-key-id\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional SubjectKeyId. */ string = get_parameter_value (para, pSUBJKEYID, 0); if (string) { for (s=string, i=0; hexdigitp (s); s++, i++) ; if (*s || (i&1)) { r = get_parameter (para, pSUBJKEYID, 0); log_error (_("line %d: invalid subject-key-id\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional extensions. */ for (seq=0; (string=get_parameter_value (para, pEXTENSION, seq)); seq++) { int okay = 0; s = strpbrk (string, " \t:"); if (s) { s++; while (spacep (s)) s++; if (*s && strchr ("nNcC", *s)) { s++; while (spacep (s)) s++; if (*s == ':') s++; if (*s) { while (spacep (s)) s++; for (i=0; hexdigitp (s); s++, i++) ; if (!((*s && *s != ':') || !i || (i&1))) okay = 1; } } } if (!okay) { r = get_parameter (para, pEXTENSION, seq); log_error (_("line %d: invalid extension syntax\n"), r? r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Create or retrieve the public key. */ if (cardkeyid) /* Take the key from the current smart card. */ { rc = gpgsm_agent_readkey (ctrl, 1, cardkeyid, &public); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: error reading key '%s' from card: %s\n"), r?r->lnr:0, cardkeyid, gpg_strerror (rc)); xfree (sigkey); xfree (cardkeyid); return rc; } } else if ((s=get_parameter_value (para, pKEYGRIP, 0))) /* Use existing key.*/ { rc = gpgsm_agent_readkey (ctrl, 0, s, &public); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: error getting key by keygrip '%s': %s\n"), r->lnr, s, gpg_strerror (rc)); xfree (sigkey); xfree (cardkeyid); return rc; } } else if (!outctrl->dryrun) /* Generate new key. */ { if (algo == GCRY_PK_RSA) { sprintf (numbuf, "%u", nbits); snprintf ((char*)keyparms, DIM (keyparms), "(6:genkey(3:rsa(5:nbits%d:%s)))", (int)strlen (numbuf), numbuf); } else if (algo == GCRY_PK_ECC) { const char *curve = get_parameter_value (para, pKEYCURVE, 0); const char *flags; if (!strcmp (curve, "Ed25519")) flags = "(5:flags5:eddsa)"; else if (!strcmp (curve, "Curve25519")) flags = "(5:flags9:djb-tweak)"; else flags = ""; snprintf ((char*)keyparms, DIM (keyparms), "(6:genkey(3:ecc(5:curve%zu:%s)%s))", strlen (curve), curve, flags); } else { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: invalid algorithm\n"), r?r->lnr:0); xfree (sigkey); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } rc = gpgsm_agent_genkey (ctrl, keyparms, &public); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: key generation failed: %s <%s>\n"), r?r->lnr:0, gpg_strerror (rc), gpg_strsource (rc)); xfree (sigkey); xfree (cardkeyid); return rc; } } if (!outctrl->dryrun) { gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; int create_cert ; create_cert = !!get_parameter_value (para, pSERIAL, 0); ctrl->pem_name = create_cert? "CERTIFICATE" : "CERTIFICATE REQUEST"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) log_error ("can't create writer: %s\n", gpg_strerror (rc)); else { rc = create_request (ctrl, para, cardkeyid, public, sigkey, writer); if (!rc) { rc = gnupg_ksba_finish_writer (b64writer); if (rc) log_error ("write failed: %s\n", gpg_strerror (rc)); else { gpgsm_status (ctrl, STATUS_KEY_CREATED, "P"); log_info ("certificate%s created\n", create_cert?"":" request"); } } gnupg_ksba_destroy_writer (b64writer); } } xfree (sigkey); xfree (public); xfree (cardkeyid); return rc; } /* Parameters are checked, the key pair has been created. Now generate the request and write it out */ static int create_request (ctrl_t ctrl, struct para_data_s *para, const char *carddirect, ksba_const_sexp_t public, ksba_const_sexp_t sigkey, ksba_writer_t writer) { ksba_certreq_t cr; gpg_error_t err; gcry_md_hd_t md; ksba_stop_reason_t stopreason; int rc = 0; const char *s, *string; unsigned int use; int seq; char *buf, *p; size_t len; char numbuf[30]; ksba_isotime_t atime; int certmode = 0; int mdalgo; + membuf_t tbsbuffer; + membuf_t *tbsmb = NULL; err = ksba_certreq_new (&cr); if (err) return err; len = gcry_sexp_canon_len (public, 0, NULL, NULL); if (get_pk_algo_from_canon_sexp (public, len) == GCRY_PK_EDDSA) - mdalgo = GCRY_MD_SHA512; - else if ((string = get_parameter_value (para, pHASHALGO, 0))) - mdalgo = gcry_md_map_name (string); + { + mdalgo = GCRY_MD_SHA512; + md = NULL; /* We sign the data and not a hash. */ + init_membuf (&tbsbuffer, 2048); + tbsmb = &tbsbuffer; + ksba_certreq_set_hash_function + (cr, (void (*)(void *, const void*,size_t))put_membuf, tbsmb); + } else - mdalgo = GCRY_MD_SHA256; - rc = gcry_md_open (&md, mdalgo, 0); - if (rc) { - log_error ("md_open failed: %s\n", gpg_strerror (rc)); - goto leave; + if ((string = get_parameter_value (para, pHASHALGO, 0))) + mdalgo = gcry_md_map_name (string); + else + mdalgo = GCRY_MD_SHA256; + rc = gcry_md_open (&md, mdalgo, 0); + if (rc) + { + log_error ("md_open failed: %s\n", gpg_strerror (rc)); + goto leave; + } + if (DBG_HASHING) + gcry_md_debug (md, "cr.cri"); + ksba_certreq_set_hash_function (cr, HASH_FNC, md); } - if (DBG_HASHING) - gcry_md_debug (md, "cr.cri"); - ksba_certreq_set_hash_function (cr, HASH_FNC, md); ksba_certreq_set_writer (cr, writer); err = ksba_certreq_add_subject (cr, get_parameter_value (para, pNAMEDN, 0)); if (err) { log_error ("error setting the subject's name: %s\n", gpg_strerror (err)); rc = err; goto leave; } for (seq=0; (s = get_parameter_value (para, pNAMEEMAIL, seq)); seq++) { buf = xtrymalloc (strlen (s) + 3); if (!buf) { rc = out_of_core (); goto leave; } *buf = '<'; strcpy (buf+1, s); strcat (buf+1, ">"); err = ksba_certreq_add_subject (cr, buf); xfree (buf); if (err) { log_error ("error setting the subject's alternate name: %s\n", gpg_strerror (err)); rc = err; goto leave; } } for (seq=0; (s = get_parameter_value (para, pNAMEDNS, seq)); seq++) { len = strlen (s); assert (len); snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len); buf = p = xtrymalloc (11 + strlen (numbuf) + len + 3); if (!buf) { rc = out_of_core (); goto leave; } p = stpcpy (p, "(8:dns-name"); p = stpcpy (p, numbuf); p = stpcpy (p, s); strcpy (p, ")"); err = ksba_certreq_add_subject (cr, buf); xfree (buf); if (err) { log_error ("error setting the subject's alternate name: %s\n", gpg_strerror (err)); rc = err; goto leave; } } for (seq=0; (s = get_parameter_value (para, pNAMEURI, seq)); seq++) { len = strlen (s); assert (len); snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len); buf = p = xtrymalloc (6 + strlen (numbuf) + len + 3); if (!buf) { rc = out_of_core (); goto leave; } p = stpcpy (p, "(3:uri"); p = stpcpy (p, numbuf); p = stpcpy (p, s); strcpy (p, ")"); err = ksba_certreq_add_subject (cr, buf); xfree (buf); if (err) { log_error ("error setting the subject's alternate name: %s\n", gpg_strerror (err)); rc = err; goto leave; } } err = ksba_certreq_set_public_key (cr, public); if (err) { log_error ("error setting the public key: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* Set key usage flags. */ use = get_parameter_uint (para, pKEYUSAGE); if (use) { unsigned int mask, pos; unsigned char der[4]; der[0] = 0x03; der[1] = 0x02; der[2] = 0; der[3] = 0; if ((use & GCRY_PK_USAGE_SIGN)) { /* For signing only we encode the bits: KSBA_KEYUSAGE_DIGITAL_SIGNATURE KSBA_KEYUSAGE_NON_REPUDIATION = 0b11 -> 0b11000000 */ der[3] |= 0xc0; } if ((use & GCRY_PK_USAGE_ENCR)) { /* For encrypt only we encode the bits: KSBA_KEYUSAGE_KEY_ENCIPHERMENT KSBA_KEYUSAGE_DATA_ENCIPHERMENT = 0b1100 -> 0b00110000 */ der[3] |= 0x30; } if ((use & GCRY_PK_USAGE_CERT)) { /* For certify only we encode the bits: KSBA_KEYUSAGE_KEY_CERT_SIGN KSBA_KEYUSAGE_CRL_SIGN = 0b1100000 -> 0b00000110 */ der[3] |= 0x06; } /* Count number of unused bits. */ for (mask=1, pos=0; pos < 8 * sizeof mask; pos++, mask <<= 1) { if ((der[3] & mask)) break; der[2]++; } err = ksba_certreq_add_extension (cr, oidstr_keyUsage, 1, der, 4); if (err) { log_error ("error setting the key usage: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* See whether we want to create an X.509 certificate. */ string = get_parameter_value (para, pSERIAL, 0); if (string) { certmode = 1; /* Store the serial number. */ if (!strcmp (string, "random")) { char snbuf[3+8+1]; memcpy (snbuf, "(8:", 3); gcry_create_nonce (snbuf+3, 8); /* Clear high bit to guarantee a positive integer. */ snbuf[3] &= 0x7f; snbuf[3+8] = ')'; err = ksba_certreq_set_serial (cr, snbuf); } else { char *hexbuf; /* Allocate a buffer large enough to prefix the string with a '0' so to have an even number of digits. Prepend two further '0' so that the binary result will have a leading 0 byte and thus can't be the representation of a negative number. Note that ksba_certreq_set_serial strips all unneeded leading 0 bytes. */ hexbuf = p = xtrymalloc (2 + 1 + strlen (string) + 1); if (!hexbuf) { err = gpg_error_from_syserror (); goto leave; } if ((strlen (string) & 1)) *p++ = '0'; *p++ = '0'; *p++ = '0'; strcpy (p, string); for (p=hexbuf, len=0; p[0] && p[1]; p += 2) ((unsigned char*)hexbuf)[len++] = xtoi_2 (p); /* Now build the S-expression. */ snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len); buf = p = xtrymalloc (1 + strlen (numbuf) + len + 1 + 1); if (!buf) { err = gpg_error_from_syserror (); xfree (hexbuf); goto leave; } p = stpcpy (stpcpy (buf, "("), numbuf); memcpy (p, hexbuf, len); p += len; strcpy (p, ")"); xfree (hexbuf); err = ksba_certreq_set_serial (cr, buf); xfree (buf); } if (err) { log_error ("error setting the serial number: %s\n", gpg_strerror (err)); goto leave; } /* Store the issuer DN. If no issuer DN is given and no signing key has been set we add the standalone extension and the basic constraints to mark it as a self-signed CA certificate. */ string = get_parameter_value (para, pISSUERDN, 0); if (string) { /* Issuer DN given. Note that this may be the same as the subject DN and thus this could as well be a self-signed certificate. However the caller needs to explicitly specify basicConstraints and so forth. */ err = ksba_certreq_set_issuer (cr, string); if (err) { log_error ("error setting the issuer DN: %s\n", gpg_strerror (err)); goto leave; } } else if (!string && !sigkey) { /* Self-signed certificate requested. Add basicConstraints and the custom GnuPG standalone extension. */ err = ksba_certreq_add_extension (cr, oidstr_basicConstraints, 1, "\x30\x03\x01\x01\xff", 5); if (err) goto leave; err = ksba_certreq_add_extension (cr, oidstr_standaloneCertificate, 0, "\x01\x01\xff", 3); if (err) goto leave; } /* Store the creation date. */ string = get_parameter_value (para, pNOTBEFORE, 0); if (string) { if (!string2isotime (atime, string)) BUG (); /* We already checked the value. */ } else gnupg_get_isotime (atime); err = ksba_certreq_set_validity (cr, 0, atime); if (err) { log_error ("error setting the creation date: %s\n", gpg_strerror (err)); goto leave; } /* Store the expire date. If it is not given, libksba inserts a default value. */ string = get_parameter_value (para, pNOTAFTER, 0); if (string) { if (!string2isotime (atime, string)) BUG (); /* We already checked the value. */ err = ksba_certreq_set_validity (cr, 1, atime); if (err) { log_error ("error setting the expire date: %s\n", gpg_strerror (err)); goto leave; } } /* Figure out the signing algorithm. If no sigkey has been given we set it to the public key to create a self-signed certificate. */ if (!sigkey) sigkey = public; { unsigned char *siginfo; + err = transform_sigval (sigkey, gcry_sexp_canon_len (sigkey, 0, NULL, NULL), mdalgo, &siginfo, NULL); if (!err) { err = ksba_certreq_set_siginfo (cr, siginfo); xfree (siginfo); } if (err) { log_error ("error setting the siginfo: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* Insert the AuthorityKeyId. */ string = get_parameter_value (para, pAUTHKEYID, 0); if (string) { char *hexbuf; /* Allocate a buffer for in-place conversion. We also add 4 extra bytes space for the tags and lengths fields. */ hexbuf = xtrymalloc (4 + strlen (string) + 1); if (!hexbuf) { err = gpg_error_from_syserror (); goto leave; } strcpy (hexbuf+4, string); for (p=hexbuf+4, len=0; p[0] && p[1]; p += 2) ((unsigned char*)hexbuf)[4+len++] = xtoi_2 (p); if (len > 125) { err = gpg_error (GPG_ERR_TOO_LARGE); xfree (hexbuf); goto leave; } hexbuf[0] = 0x30; /* Tag for a Sequence. */ hexbuf[1] = len+2; hexbuf[2] = 0x80; /* Context tag for an implicit Octet string. */ hexbuf[3] = len; err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier, 0, hexbuf, 4+len); xfree (hexbuf); if (err) { log_error ("error setting the authority-key-id: %s\n", gpg_strerror (err)); goto leave; } } /* Insert the SubjectKeyId. */ string = get_parameter_value (para, pSUBJKEYID, 0); if (string) { char *hexbuf; /* Allocate a buffer for in-place conversion. We also add 2 extra bytes space for the tag and length field. */ hexbuf = xtrymalloc (2 + strlen (string) + 1); if (!hexbuf) { err = gpg_error_from_syserror (); goto leave; } strcpy (hexbuf+2, string); for (p=hexbuf+2, len=0; p[0] && p[1]; p += 2) ((unsigned char*)hexbuf)[2+len++] = xtoi_2 (p); if (len > 127) { err = gpg_error (GPG_ERR_TOO_LARGE); xfree (hexbuf); goto leave; } hexbuf[0] = 0x04; /* Tag for an Octet string. */ hexbuf[1] = len; err = ksba_certreq_add_extension (cr, oidstr_subjectKeyIdentifier, 0, hexbuf, 2+len); xfree (hexbuf); if (err) { log_error ("error setting the subject-key-id: %s\n", gpg_strerror (err)); goto leave; } } /* Insert additional extensions. */ for (seq=0; (string = get_parameter_value (para, pEXTENSION, seq)); seq++) { char *hexbuf; char *oidstr; int crit = 0; s = strpbrk (string, " \t:"); if (!s) { err = gpg_error (GPG_ERR_INTERNAL); goto leave; } oidstr = xtrymalloc (s - string + 1); if (!oidstr) { err = gpg_error_from_syserror (); goto leave; } memcpy (oidstr, string, (s-string)); oidstr[(s-string)] = 0; s++; while (spacep (s)) s++; if (!*s) { err = gpg_error (GPG_ERR_INTERNAL); xfree (oidstr); goto leave; } if (strchr ("cC", *s)) crit = 1; s++; while (spacep (s)) s++; if (*s == ':') s++; while (spacep (s)) s++; hexbuf = xtrystrdup (s); if (!hexbuf) { err = gpg_error_from_syserror (); xfree (oidstr); goto leave; } for (p=hexbuf, len=0; p[0] && p[1]; p += 2) ((unsigned char*)hexbuf)[len++] = xtoi_2 (p); err = ksba_certreq_add_extension (cr, oidstr, crit, hexbuf, len); xfree (oidstr); xfree (hexbuf); } } else sigkey = public; do { err = ksba_certreq_build (cr, &stopreason); if (err) { log_error ("ksba_certreq_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } if (stopreason == KSBA_SR_NEED_SIG) { gcry_sexp_t s_pkey; size_t n; unsigned char grip[20]; char hexgrip[41]; unsigned char *sigval, *newsigval; size_t siglen; + void *tbsdata; + size_t tbsdatalen; n = gcry_sexp_canon_len (sigkey, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); rc = gpg_error (GPG_ERR_BUG); goto leave; } rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)sigkey, n); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); goto leave; } if ( !gcry_pk_get_keygrip (s_pkey, grip) ) { rc = gpg_error (GPG_ERR_GENERAL); log_error ("can't figure out the keygrip\n"); gcry_sexp_release (s_pkey); goto leave; } gcry_sexp_release (s_pkey); bin2hex (grip, 20, hexgrip); log_info ("about to sign the %s for key: &%s\n", certmode? "certificate":"CSR", hexgrip); if (carddirect && !certmode) - rc = gpgsm_scd_pksign (ctrl, carddirect, NULL, - gcry_md_read (md, mdalgo), - gcry_md_get_algo_dlen (mdalgo), - mdalgo, - &sigval, &siglen); + { + if (tbsmb) + { + tbsdata = get_membuf (tbsmb, &tbsdatalen); + tbsmb = NULL; + if (!tbsdata) + rc = gpg_error_from_syserror (); + else + rc = gpgsm_scd_pksign (ctrl, carddirect, NULL, + tbsdata, tbsdatalen, 0, + &sigval, &siglen); + xfree (tbsdata); + } + else + rc = gpgsm_scd_pksign (ctrl, carddirect, NULL, + gcry_md_read (md, mdalgo), + gcry_md_get_algo_dlen (mdalgo), + mdalgo, + &sigval, &siglen); + } else { char *orig_codeset; char *desc; orig_codeset = i18n_switchto_utf8 (); desc = percent_plus_escape (_("To complete this certificate request please enter" " the passphrase for the key you just created once" " more.\n")); i18n_switchback (orig_codeset); - rc = gpgsm_agent_pksign (ctrl, hexgrip, desc, - gcry_md_read(md, mdalgo), - gcry_md_get_algo_dlen (mdalgo), - mdalgo, - &sigval, &siglen); + if (tbsmb) + { + tbsdata = get_membuf (tbsmb, &tbsdatalen); + tbsmb = NULL; + if (!tbsdata) + rc = gpg_error_from_syserror (); + else + rc = gpgsm_agent_pksign (ctrl, hexgrip, desc, + tbsdata, tbsdatalen, 0, + + &sigval, &siglen); + xfree (tbsdata); + } + else + rc = gpgsm_agent_pksign (ctrl, hexgrip, desc, + gcry_md_read(md, mdalgo), + gcry_md_get_algo_dlen (mdalgo), + mdalgo, + &sigval, &siglen); xfree (desc); } if (rc) { log_error ("signing failed: %s\n", gpg_strerror (rc)); goto leave; } err = transform_sigval (sigval, siglen, mdalgo, &newsigval, NULL); xfree (sigval); if (!err) { err = ksba_certreq_set_sig_val (cr, newsigval); xfree (newsigval); } if (err) { log_error ("failed to store the sig_val: %s\n", gpg_strerror (err)); rc = err; goto leave; } } } while (stopreason != KSBA_SR_READY); leave: + if (tbsmb) + xfree (get_membuf (tbsmb, NULL)); gcry_md_close (md); ksba_certreq_release (cr); return rc; } /* Create a new key by reading the parameters from IN_FP. Multiple keys may be created */ int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream) { int rc; rc = read_parameters (ctrl, in_stream, out_stream); if (rc) { log_error ("error creating certificate request: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc)); goto leave; } leave: return rc; } diff --git a/sm/misc.c b/sm/misc.c index 954529390..99f6da63c 100644 --- a/sm/misc.c +++ b/sm/misc.c @@ -1,266 +1,286 @@ /* misc.c - Miscellaneous functions * Copyright (C) 2004, 2009, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include "gpgsm.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "../common/tlv.h" #include "../common/sexp-parse.h" /* Setup the environment so that the pinentry is able to get all required information. This is used prior to an exec of the protect-tool. */ void setup_pinentry_env (void) { #ifndef HAVE_W32_SYSTEM char *lc; const char *name, *value; int iterator; /* Try to make sure that GPG_TTY has been set. This is needed if we call for example the protect-tools with redirected stdin and thus it won't be able to ge a default by itself. Try to do it here but print a warning. */ value = session_env_getenv (opt.session_env, "GPG_TTY"); if (value) gnupg_setenv ("GPG_TTY", value, 1); else if (!(lc=getenv ("GPG_TTY")) || !*lc) { log_error (_("GPG_TTY has not been set - " "using maybe bogus default\n")); lc = gnupg_ttyname (0); if (!lc) lc = "/dev/tty"; gnupg_setenv ("GPG_TTY", lc, 1); } if (opt.lc_ctype) gnupg_setenv ("LC_CTYPE", opt.lc_ctype, 1); #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) else if ( (lc = setlocale (LC_CTYPE, "")) ) gnupg_setenv ("LC_CTYPE", lc, 1); #endif if (opt.lc_messages) gnupg_setenv ("LC_MESSAGES", opt.lc_messages, 1); #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) else if ( (lc = setlocale (LC_MESSAGES, "")) ) gnupg_setenv ("LC_MESSAGES", lc, 1); #endif iterator = 0; while ((name = session_env_list_stdenvnames (&iterator, NULL))) { if (!strcmp (name, "GPG_TTY")) continue; /* Already set. */ value = session_env_getenv (opt.session_env, name); if (value) gnupg_setenv (name, value, 1); } #endif /*!HAVE_W32_SYSTEM*/ } /* Transform a sig-val style s-expression as returned by Libgcrypt to one which includes an algorithm identifier encoding the public key and the hash algorithm. The public key algorithm is taken directly from SIGVAL and the hash algorithm is given by MDALGO. This is required because X.509 merges the public key algorithm and the hash algorithm into one OID but Libgcrypt is not aware of that. The function ignores missing parameters so that it can also be used to create an siginfo value as expected by ksba_certreq_set_siginfo. To create a siginfo s-expression a public-key s-expression may be - used instead of a sig-val. We only support RSA for now. */ + used instead of a sig-val. */ gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, unsigned char **r_newsigval, size_t *r_newsigvallen) { gpg_error_t err; const unsigned char *buf, *tok; size_t buflen, toklen; int depth, last_depth1, last_depth2, pkalgo; int is_pubkey = 0; const unsigned char *rsa_s, *ecc_r, *ecc_s; size_t rsa_s_len, ecc_r_len, ecc_s_len; const char *oid; gcry_sexp_t sexp; + const char *eddsa_curve = NULL; rsa_s = ecc_r = ecc_s = NULL; rsa_s_len = ecc_r_len = ecc_s_len = 0; *r_newsigval = NULL; if (r_newsigvallen) *r_newsigvallen = 0; buf = sigval; buflen = sigvallen; depth = 0; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && toklen == 7 && !memcmp ("sig-val", tok, toklen)) ; else if (tok && toklen == 10 && !memcmp ("public-key", tok, toklen)) is_pubkey = 1; else return gpg_error (GPG_ERR_UNKNOWN_SEXP); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (!tok) return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); if (toklen == 3 && !memcmp ("rsa", tok, 3)) pkalgo = GCRY_PK_RSA; + else if (toklen == 3 && !memcmp ("ecc", tok, 3)) + pkalgo = GCRY_PK_ECC; else if (toklen == 5 && !memcmp ("ecdsa", tok, 5)) pkalgo = GCRY_PK_ECC; else if (toklen == 5 && !memcmp ("eddsa", tok, 5)) pkalgo = GCRY_PK_EDDSA; else return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); last_depth1 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth1) { if (tok) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && toklen == 1) { const unsigned char **mpi = NULL; size_t *mpi_len = NULL; switch (*tok) { case 's': if (pkalgo == GCRY_PK_RSA) { mpi = &rsa_s; mpi_len = &rsa_s_len; } else if (pkalgo == GCRY_PK_ECC || pkalgo == GCRY_PK_EDDSA) { mpi = &ecc_s; mpi_len = &ecc_s_len; } break; case 'r': mpi = &ecc_r; mpi_len = &ecc_r_len; break; default: mpi = NULL; mpi_len = NULL; break; } if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && mpi) { *mpi = tok; *mpi_len = toklen; } } + else if (toklen == 5 && !memcmp (tok, "curve", 5)) + { + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((toklen == 7 && !memcmp (tok, "Ed25519", 7)) + || (toklen == 22 && !memcmp (tok, "1.3.6.1.4.1.11591.15.1", 22)) + || (toklen == 11 && !memcmp (tok, "1.3.101.112", 11))) + eddsa_curve = "1.3.101.112"; + else if ((toklen == 5 && !memcmp (tok, "Ed448", 5)) + || (toklen == 11 && !memcmp (tok, "1.3.101.113", 11))) + eddsa_curve = "1.3.101.113"; + } /* Skip to the end of the list. */ last_depth2 = depth; while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) && depth && depth >= last_depth2) ; if (err) return err; } if (err) return err; - /* Map the hash algorithm to an OID. */ - if (mdalgo < 0 || mdalgo > (1<<15) || pkalgo < 0 || pkalgo > (1<<15)) - return gpg_error (GPG_ERR_DIGEST_ALGO); - - switch (mdalgo | (pkalgo << 16)) + if (eddsa_curve) + oid = eddsa_curve; + else { - case GCRY_MD_SHA1 | (GCRY_PK_RSA << 16): - oid = "1.2.840.113549.1.1.5"; /* sha1WithRSAEncryption */ - break; + /* Map the hash algorithm to an OID. */ + if (mdalgo < 0 || mdalgo > (1<<15) || pkalgo < 0 || pkalgo > (1<<15)) + return gpg_error (GPG_ERR_DIGEST_ALGO); - case GCRY_MD_SHA256 | (GCRY_PK_RSA << 16): - oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */ - break; + switch (mdalgo | (pkalgo << 16)) + { + case GCRY_MD_SHA1 | (GCRY_PK_RSA << 16): + oid = "1.2.840.113549.1.1.5"; /* sha1WithRSAEncryption */ + break; - case GCRY_MD_SHA384 | (GCRY_PK_RSA << 16): - oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */ - break; + case GCRY_MD_SHA256 | (GCRY_PK_RSA << 16): + oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */ + break; - case GCRY_MD_SHA512 | (GCRY_PK_RSA << 16): - oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */ - break; + case GCRY_MD_SHA384 | (GCRY_PK_RSA << 16): + oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */ + break; - case GCRY_MD_SHA224 | (GCRY_PK_ECC << 16): - oid = "1.2.840.10045.4.3.1"; /* ecdsa-with-sha224 */ - break; + case GCRY_MD_SHA512 | (GCRY_PK_RSA << 16): + oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */ + break; - case GCRY_MD_SHA256 | (GCRY_PK_ECC << 16): - oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-sha256 */ - break; + case GCRY_MD_SHA224 | (GCRY_PK_ECC << 16): + oid = "1.2.840.10045.4.3.1"; /* ecdsa-with-sha224 */ + break; - case GCRY_MD_SHA384 | (GCRY_PK_ECC << 16): - oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-sha384 */ - break; + case GCRY_MD_SHA256 | (GCRY_PK_ECC << 16): + oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-sha256 */ + break; - case GCRY_MD_SHA512 | (GCRY_PK_ECC << 16): - oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-sha512 */ - break; + case GCRY_MD_SHA384 | (GCRY_PK_ECC << 16): + oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-sha384 */ + break; - case GCRY_MD_SHA512 | (GCRY_PK_EDDSA << 16): - oid = "1.3.101.112"; /* ed25519 */ - break; + case GCRY_MD_SHA512 | (GCRY_PK_ECC << 16): + oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-sha512 */ + break; - default: - return gpg_error (GPG_ERR_DIGEST_ALGO); + case GCRY_MD_SHA512 | (GCRY_PK_EDDSA << 16): + oid = "1.3.101.112"; /* ed25519 */ + break; + + default: + return gpg_error (GPG_ERR_DIGEST_ALGO); + } } if (is_pubkey) err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s))", oid); else if (pkalgo == GCRY_PK_RSA) err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(s%b)))", oid, (int)rsa_s_len, rsa_s); else if (pkalgo == GCRY_PK_ECC || pkalgo == GCRY_PK_EDDSA) err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(r%b)(s%b)))", oid, (int)ecc_r_len, ecc_r, (int)ecc_s_len, ecc_s); if (err) return err; err = make_canon_sexp (sexp, r_newsigval, r_newsigvallen); gcry_sexp_release (sexp); return err; }