Page MenuHome GnuPG

No OneTemporary

diff --git a/sm/call-agent.c b/sm/call-agent.c
index 16fb10901..370cd8e30 100644
--- a/sm/call-agent.c
+++ b/sm/call-agent.c
@@ -1,1755 +1,1755 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include "gpgsm.h"
#include <gcrypt.h>
#include <assuan.h>
#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;
};
/* An object and variable to cache ISTRUSTED calls. The cache is
* global and reset with each mark trusted. We also have a disabled
* flag here in case the gpg-agent did not allow us to query all
* trusted keys at once. */
struct istrusted_cache_s
{
struct istrusted_cache_s *next;
struct rootca_flags_s flags; /* The flags of this fingerprint. */
char fpr[1]; /* The fingerprint of the trusted key in hex format. */
};
typedef struct istrusted_cache_s *istrusted_cache_t;
static istrusted_cache_t istrusted_cache;
static int istrusted_cache_valid;
static int istrusted_cache_disabled;
/* Flag indicating that we can't use the keyinfo cache at all. The
* actual cache is stored in CTRL. */
static int keyinfo_cache_disabled;
static void
flush_istrusted_cache (void)
{
istrusted_cache_t mycache;
/* First unlink the cache to be npth safe. Note that we don't clear
* the the disabled flag - this is considered a permantent error. */
mycache = istrusted_cache;
istrusted_cache = NULL;
istrusted_cache_valid = 0;
while (mycache)
{
istrusted_cache_t next = mycache->next;
xfree (mycache);
mycache = next;
}
}
/* Release all items in *CACHEP and set CACHEP to NULL */
static void
release_a_keyinfo_cache (keyinfo_cache_item_t *cachep)
{
keyinfo_cache_item_t mycache;
/* First unlink the cache to be npth safe. */
mycache = *cachep;
*cachep = NULL;
while (mycache)
{
keyinfo_cache_item_t next = mycache->next;
xfree (mycache);
mycache = next;
}
}
/* Flush the keyinfo cache for the session CTRL. */
void
gpgsm_flush_keyinfo_cache (ctrl_t ctrl)
{
ctrl->keyinfo_cache_valid = 0;
release_a_keyinfo_cache (&ctrl->keyinfo_cache);
}
/* 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)
{
return warn_server_version_mismatch (ctx, servername, mode,
gpgsm_status2, ctrl,
!opt.quiet);
}
/* 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?ASSHELP_FLAG_AUTOSTART:0,
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 ();
assuan_begin_confidential (parm->ctx);
err = assuan_send_data (parm->ctx, s, strlen (s));
assuan_end_confidential (parm->ctx);
}
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. 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 (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;
}
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;
log_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,
+ int use_kem, 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;
log_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",
+ rc = assuan_transact (agent_ctx, use_kem? "PKDECRYPT --kem=CMS": "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 new key */
gpg_error_t
gpgsm_agent_genkey (ctrl_t ctrl, int no_protection,
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;
gnupg_isotime_t timebuf;
char line[ASSUAN_LINELENGTH];
*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);
gnupg_get_isotime (timebuf);
snprintf (line, sizeof line, "GENKEY%s --timestamp=%s",
no_protection? " --no-protection":"",
timebuf);
rc = assuan_transact (agent_ctx, line,
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;
}
struct istrusted_status_parm_s
{
struct rootca_flags_s flags;
istrusted_cache_t cache;
};
static gpg_error_t
istrusted_status_cb (void *opaque, const char *line)
{
struct istrusted_status_parm_s *parm = opaque;
const char *s;
if ((s = has_leading_keyword (line, "TRUSTLISTFLAG")))
{
line = s;
if (has_leading_keyword (line, "relax"))
parm->flags.relax = 1;
else if (has_leading_keyword (line, "cm"))
parm->flags.chain_model = 1;
else if (has_leading_keyword (line, "qual"))
parm->flags.qualified = 1;
else if (has_leading_keyword (line, "noconsent"))
parm->flags.noconsent = 1;
else if (has_leading_keyword (line, "de-vs"))
parm->flags.de_vs = 1;
/* Copy the current flags to the current list item. */
if (parm->cache)
parm->cache->flags = parm->flags;
}
else if ((s = has_leading_keyword (line, "TRUSTLISTFPR")) && *s)
{
/* We see this only with the "LISTTRUSTED --status" command but
* not with ISTRUSTED. Thus the cache will only be filled by
* the former command. */
istrusted_cache_t ci;
ci = xtrymalloc (sizeof *ci + strlen (s));
if (!ci)
return gpg_error_from_syserror ();
strcpy (ci->fpr, s);
memset (&ci->flags, 0, sizeof ci->flags);
ci->next = parm->cache;
parm->cache = ci;
/* Also need to clear the parm's flags which will be copied to
* the cache. */
memset (&parm->flags, 0, sizeof ci->flags);
}
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];
char *fpr_buffer = NULL;
struct istrusted_status_parm_s parm;
istrusted_cache_t ci;
memset (rootca_flags, 0, sizeof *rootca_flags);
memset (&parm, 0, sizeof parm);
if (cert && hexfpr)
return gpg_error (GPG_ERR_INV_ARG);
rc = start_agent (ctrl);
if (rc)
return rc;
if (!hexfpr)
{
fpr_buffer = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
if (!fpr_buffer)
{
log_error ("error getting the fingerprint\n");
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
hexfpr = fpr_buffer;
}
/* First try to get the info from the cache. */
if ((opt.compat_flags & COMPAT_NO_KEYINFO_CACHE))
istrusted_cache_disabled = 1;
if (!istrusted_cache_disabled && !istrusted_cache_valid)
{
/* Cache is empty - fill it. */
rc = assuan_transact (agent_ctx, "LISTTRUSTED --status",
NULL, NULL, NULL, NULL,
istrusted_status_cb, &parm);
istrusted_cache = parm.cache;
parm.cache = NULL;
if (rc)
{
if (gpg_err_code (rc) != GPG_ERR_FORBIDDEN)
log_info ("filling istrusted cache failed: %s\n",
gpg_strerror (rc));
istrusted_cache_disabled = 1;
flush_istrusted_cache ();
rc = 0; /* Fallback to single requests. */
}
else
istrusted_cache_valid = 1;
}
if (istrusted_cache_valid)
{
for (ci = istrusted_cache; ci; ci = ci->next)
if (!strcmp (ci->fpr, hexfpr))
break; /* Found. */
if (ci)
{
*rootca_flags = ci->flags;
rootca_flags->valid = 1;
rc = 0;
}
else
rc = gpg_error (GPG_ERR_NOT_TRUSTED);
goto leave;
}
snprintf (line, DIM(line), "ISTRUSTED %s", hexfpr);
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
istrusted_status_cb, &parm);
if (!rc)
{
*rootca_flags = parm.flags;
rootca_flags->valid = 1;
}
leave:
xfree (fpr_buffer);
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);
/* Marktrusted changes the trustlist and thus we need to flush the
* cache. */
if (!rc)
flush_istrusted_cache ();
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;
char *string, *p, *pend;
strlist_t sl;
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;
}
/* Ignore certificates matching certain extended usage flags. */
rc = ksba_cert_get_ext_key_usages (cert, &string);
if (!rc)
{
p = string;
while (p && (pend=strchr (p, ':')))
{
*pend++ = 0;
for (sl=opt.ignore_cert_with_oid;
sl && strcmp (sl->d, p); sl = sl->next)
;
if (sl)
{
if (opt.verbose)
log_info ("certificate ignored due to OID %s\n", sl->d);
goto leave;
}
p = pend;
if ((p = strchr (p, '\n')))
p++;
}
}
else if (gpg_err_code (rc) != GPG_ERR_NO_DATA)
log_error (_("error getting key usage information: %s\n"),
gpg_strerror (rc));
xfree (string);
string = NULL;
/* We do not store a certificate 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");
}
}
leave:
xfree (string);
string = NULL;
ksba_cert_release (cert);
init_membuf (parm->data, 4096);
return 0;
}
/* Call the agent to learn about a smartcard. If SERIALNO is not NULL
* switch to the card with that s/n first. */
int
gpgsm_agent_learn (ctrl_t ctrl, const char *serialno)
{
int rc;
struct learn_parm_s learn_parm;
membuf_t data;
size_t len;
char line[ASSUAN_LINELENGTH];
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;
snprintf (line, sizeof line, "LEARN --send%s%s",
serialno? " -- ":"",
serialno? serialno:"");
rc = assuan_transact (agent_ctx, line,
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;
}
struct keyinfo_status_parm_s
{
char *serialno;
int fill_mode; /* True if we want to fill the cache. */
keyinfo_cache_item_t cache;
};
static gpg_error_t
keyinfo_status_cb (void *opaque, const char *line)
{
struct keyinfo_status_parm_s *parm = opaque;
const char *s0, *s, *s2;
if ((s0 = has_leading_keyword (line, "KEYINFO"))
&& (!parm->serialno || parm->fill_mode))
{
s = strchr (s0, ' ');
xfree (parm->serialno);
parm->serialno = NULL;
if (s && s[1] == 'T' && s[2] == ' ' && s[3])
{
s += 3;
s2 = strchr (s, ' ');
if ( s2 > s )
{
parm->serialno = xtrymalloc ((s2 - s)+1);
if (parm->serialno)
{
memcpy (parm->serialno, s, s2 - s);
parm->serialno[s2 - s] = 0;
}
}
}
if (parm->fill_mode && *s0)
{
keyinfo_cache_item_t ci;
size_t n;
n = s? (s - s0) : strlen (s0);
ci = xtrymalloc (sizeof *ci + n);
if (!ci)
return gpg_error_from_syserror ();
memcpy (ci->hexgrip, s0, n);
ci->hexgrip[n] = 0;
ci->serialno = parm->serialno;
parm->serialno = NULL;
ci->next = parm->cache;
parm->cache = ci;
}
}
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.
*
* Take care: The cache is currently only used in the key listing and
* it should not interfere with import or creation of new keys because
* we assume that is done by another process. However we assume that
* in server mode the key listing is not directly followed by an import
* and another key listing.
*/
gpg_error_t
gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno)
{
gpg_error_t err;
char line[ASSUAN_LINELENGTH];
keyinfo_cache_item_t ci;
struct keyinfo_status_parm_s parm = { NULL };
*r_serialno = NULL;
err = start_agent (ctrl);
if (err)
return err;
if (!hexkeygrip || strlen (hexkeygrip) != 40)
return gpg_error (GPG_ERR_INV_VALUE);
/* First try to fill the cache. */
if ((opt.compat_flags & COMPAT_NO_KEYINFO_CACHE))
keyinfo_cache_disabled = 1;
if (!keyinfo_cache_disabled && !ctrl->keyinfo_cache_valid)
{
parm.fill_mode = 1;
err = assuan_transact (agent_ctx, "KEYINFO --list",
NULL, NULL, NULL, NULL,
keyinfo_status_cb, &parm);
if (err)
{
if (gpg_err_code (err) != GPG_ERR_FORBIDDEN)
log_error ("filling keyinfo cache failed: %s\n",
gpg_strerror (err));
keyinfo_cache_disabled = 1;
release_a_keyinfo_cache (&parm.cache);
err = 0; /* Fallback to single requests. */
}
else
{
ctrl->keyinfo_cache_valid = 1;
ctrl->keyinfo_cache = parm.cache;
parm.cache = NULL;
}
}
/* Then consult the cache or send a query */
if (ctrl->keyinfo_cache_valid)
{
for (ci = ctrl->keyinfo_cache; ci; ci = ci->next)
if (!strcmp (hexkeygrip, ci->hexgrip))
break;
if (ci)
{
xfree (parm.serialno);
parm.serialno = NULL;
err = 0;
if (ci->serialno)
{
parm.serialno = xtrystrdup (ci->serialno);
if (!parm.serialno)
err = gpg_error_from_syserror ();
}
}
else
err = gpg_error (GPG_ERR_NOT_FOUND);
}
else
{
snprintf (line, DIM(line), "KEYINFO %s", hexkeygrip);
parm.fill_mode = 0;
err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
keyinfo_status_cb, &parm);
}
if (!err && parm.serialno)
{
/* Sanity check for bad characters. */
if (strpbrk (parm.serialno, ":\n\r"))
err = gpg_error (GPG_ERR_INV_VALUE);
}
if (err)
xfree (parm.serialno);
else
*r_serialno = parm.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;
int wasconf;
*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":"",
arg4);
xfree (arg4);
init_membuf_secure (&data, 64);
wasconf = assuan_get_flag (agent_ctx, ASSUAN_CONFIDENTIAL);
assuan_begin_confidential (agent_ctx);
err = assuan_transact (agent_ctx, line,
put_membuf_cb, &data,
default_inq_cb, &inq_parm, NULL, NULL);
if (!wasconf)
assuan_end_confidential (agent_ctx);
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;
gnupg_isotime_t timebuf;
char line[ASSUAN_LINELENGTH];
err = start_agent (ctrl);
if (err)
return err;
parm.ctrl = ctrl;
parm.ctx = agent_ctx;
parm.key = key;
parm.keylen = keylen;
gnupg_get_isotime (timebuf);
snprintf (line, sizeof line, "IMPORT_KEY --timestamp=%s", timebuf);
err = assuan_transact (agent_ctx, line,
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/decrypt.c b/sm/decrypt.c
index 81294eeff..b3867e072 100644
--- a/sm/decrypt.c
+++ b/sm/decrypt.c
@@ -1,1517 +1,1531 @@
/* decrypt.c - Decrypt a message
* Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc.
* Copyright (C) 2001-2019 Werner Koch
* Copyright (C) 2015-2021 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 <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include "gpgsm.h"
#include <gcrypt.h>
#include <ksba.h>
#include "keydb.h"
#include "../common/i18n.h"
#include "../common/tlv.h"
#include "../common/compliance.h"
struct decrypt_filter_parm_s
{
int algo;
int mode;
int blklen;
gcry_cipher_hd_t hd;
char iv[16];
size_t ivlen;
int any_data; /* did we push anything through the filter at all? */
unsigned char lastblock[16]; /* to strip the padding we have to
keep this one */
char helpblock[16]; /* needed because there is no block buffering in
libgcrypt (yet) */
int helpblocklen;
int is_de_vs; /* Helper to track CO_DE_VS state. */
};
/* 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 char *
string_from_gcry_buffer (gcry_buffer_t *buffer)
{
char *string;
string = xtrymalloc (buffer->len + 1);
if (!string)
return NULL;
memcpy (string, buffer->data, buffer->len);
string[buffer->len] = 0;
return string;
}
/* Helper to construct and hash the
* ECC-CMS-SharedInfo ::= SEQUENCE {
* keyInfo AlgorithmIdentifier,
* entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL,
* suppPubInfo [2] EXPLICIT OCTET STRING }
* as described in RFC-5753, 7.2. */
static gpg_error_t
hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str,
unsigned int keylen,
const void *ukm, unsigned int ukmlen)
{
gpg_error_t err;
void *p;
unsigned char *oid;
size_t n, oidlen, toidlen, tkeyinfo, tukmlen, tsupppubinfo;
unsigned char keylenbuf[6];
membuf_t mb = MEMBUF_ZERO;
err = ksba_oid_from_str (wrap_algo_str, &oid, &oidlen);
if (err)
return err;
toidlen = get_tlv_length (CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen);
tkeyinfo = get_tlv_length (CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen);
tukmlen = ukm? get_tlv_length (CLASS_CONTEXT, 0, 1, ukmlen) : 0;
keylen *= 8;
keylenbuf[0] = TAG_OCTET_STRING;
keylenbuf[1] = 4;
keylenbuf[2] = (keylen >> 24);
keylenbuf[3] = (keylen >> 16);
keylenbuf[4] = (keylen >> 8);
keylenbuf[5] = keylen;
tsupppubinfo = get_tlv_length (CLASS_CONTEXT, 2, 1, sizeof keylenbuf);
put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1,
tkeyinfo + tukmlen + tsupppubinfo);
put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1,
toidlen);
put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen);
put_membuf (&mb, oid, oidlen);
ksba_free (oid);
if (ukm)
{
put_tlv_to_membuf (&mb, CLASS_CONTEXT, 0, 1, ukmlen);
put_membuf (&mb, ukm, ukmlen);
}
put_tlv_to_membuf (&mb, CLASS_CONTEXT, 2, 1, sizeof keylenbuf);
put_membuf (&mb, keylenbuf, sizeof keylenbuf);
p = get_membuf (&mb, &n);
if (!p)
return gpg_error_from_syserror ();
gcry_md_write (hash_hd, p, n);
xfree (p);
return 0;
}
/* Derive a KEK (key wrapping key) using (SECRET,SECRETLEN), an
* optional (UKM,ULMLEN), the wrap algorithm WRAP_ALGO_STR in decimal
* dotted form, and the hash algorithm HASH_ALGO. On success a key of
* length KEYLEN is stored at KEY. */
gpg_error_t
ecdh_derive_kek (unsigned char *key, unsigned int keylen,
int hash_algo, const char *wrap_algo_str,
const void *secret, unsigned int secretlen,
const void *ukm, unsigned int ukmlen)
{
gpg_error_t err = 0;
unsigned int hashlen;
gcry_md_hd_t hash_hd;
unsigned char counter;
unsigned int n, ncopy;
hashlen = gcry_md_get_algo_dlen (hash_algo);
if (!hashlen)
return gpg_error (GPG_ERR_INV_ARG);
err = gcry_md_open (&hash_hd, hash_algo, 0);
if (err)
return err;
/* According to SEC1 3.6.1 we should check that
* SECRETLEN + UKMLEN + 4 < maxhashlen
* However, we have no practical limit on the hash length and thus
* there is no point in checking this. The second check that
* KEYLEN < hashlen*(2^32-1)
* is obviously also not needed.
*/
for (n=0, counter=1; n < keylen; counter++)
{
if (counter > 1)
gcry_md_reset (hash_hd);
gcry_md_write (hash_hd, secret, secretlen);
gcry_md_write (hash_hd, "\x00\x00\x00", 3); /* MSBs of counter */
gcry_md_write (hash_hd, &counter, 1);
err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen,
ukm, ukmlen);
if (err)
break;
gcry_md_final (hash_hd);
if (n + hashlen > keylen)
ncopy = keylen - n;
else
ncopy = hashlen;
memcpy (key+n, gcry_md_read (hash_hd, 0), ncopy);
n += ncopy;
}
gcry_md_close (hash_hd);
return err;
}
-/* This function will modify SECRET. NBITS is the size of the curve
- * which which we took from the certificate. */
-static gpg_error_t
-ecdh_decrypt (unsigned char *secret, size_t secretlen,
- unsigned int nbits, gcry_sexp_t enc_val,
- unsigned char **r_result, unsigned int *r_resultlen)
-{
- gpg_error_t err;
- gcry_buffer_t ioarray[4] = { {0}, {0}, {0}, {0} };
- char *encr_algo_str = NULL;
- char *wrap_algo_str = NULL;
- int hash_algo, cipher_algo;
- const unsigned char *ukm; /* Alias for ioarray[2]. */
- unsigned int ukmlen;
- const unsigned char *data; /* Alias for ioarray[3]. */
- unsigned int datalen;
- unsigned int keylen;
- unsigned char key[32];
- gcry_cipher_hd_t cipher_hd = NULL;
- unsigned char *result = NULL;
- unsigned int resultlen;
-
- *r_resultlen = 0;
- *r_result = NULL;
-
- /* Extract X from SECRET; this is the actual secret. Unless a
- * smartcard diretcly returns X, it must be in the format of:
- *
- * 04 || X || Y
- * 40 || X
- * 41 || X
- */
- if (secretlen < 2)
- return gpg_error (GPG_ERR_BAD_DATA);
- if (secretlen == (nbits+7)/8)
- ; /* Matches curve length - this is already the X coordinate. */
- else if (*secret == 0x04)
- {
- secretlen--;
- memmove (secret, secret+1, secretlen);
- if ((secretlen & 1))
- return gpg_error (GPG_ERR_BAD_DATA);
- secretlen /= 2;
- }
- else if (*secret == 0x40 || *secret == 0x41)
- {
- secretlen--;
- memmove (secret, secret+1, secretlen);
- }
- else
- return gpg_error (GPG_ERR_BAD_DATA);
- if (!secretlen)
- return gpg_error (GPG_ERR_BAD_DATA);
-
- if (DBG_CRYPTO)
- log_printhex (secret, secretlen, "ECDH X ..:");
-
- /* We have now the shared secret bytes in (SECRET,SECRETLEN). Now
- * we will compute the KEK using a value derived from the secret
- * bytes. */
- err = gcry_sexp_extract_param (enc_val, "enc-val",
- "&'encr-algo''wrap-algo''ukm'?s",
- ioarray+0, ioarray+1,
- ioarray+2, ioarray+3, NULL);
- if (err)
- {
- log_error ("extracting ECDH parameter failed: %s\n", gpg_strerror (err));
- goto leave;
- }
- encr_algo_str = string_from_gcry_buffer (ioarray);
- if (!encr_algo_str)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- wrap_algo_str = string_from_gcry_buffer (ioarray+1);
- if (!wrap_algo_str)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
- ukm = ioarray[2].data;
- ukmlen = ioarray[2].len;
- data = ioarray[3].data;
- datalen = ioarray[3].len;
-
- /* Check parameters. */
- if (DBG_CRYPTO)
- {
- log_debug ("encr_algo: %s\n", encr_algo_str);
- log_debug ("wrap_algo: %s\n", wrap_algo_str);
- log_printhex (ukm, ukmlen, "ukm .....:");
- log_printhex (data, datalen, "data ....:");
- }
-
- if (!strcmp (encr_algo_str, "1.3.132.1.11.1"))
- {
- /* dhSinglePass-stdDH-sha256kdf-scheme */
- hash_algo = GCRY_MD_SHA256;
- }
- else if (!strcmp (encr_algo_str, "1.3.132.1.11.2"))
- {
- /* dhSinglePass-stdDH-sha384kdf-scheme */
- hash_algo = GCRY_MD_SHA384;
- }
- else if (!strcmp (encr_algo_str, "1.3.132.1.11.3"))
- {
- /* dhSinglePass-stdDH-sha512kdf-scheme */
- hash_algo = GCRY_MD_SHA512;
- }
- else if (!strcmp (encr_algo_str, "1.3.133.16.840.63.0.2"))
- {
- /* dhSinglePass-stdDH-sha1kdf-scheme */
- hash_algo = GCRY_MD_SHA1;
- }
- else
- {
- err = gpg_error (GPG_ERR_PUBKEY_ALGO);
- goto leave;
- }
-
- if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.5"))
- {
- cipher_algo = GCRY_CIPHER_AES128;
- keylen = 16;
- }
- else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.25"))
- {
- cipher_algo = GCRY_CIPHER_AES192;
- keylen = 24;
- }
- else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.45"))
- {
- cipher_algo = GCRY_CIPHER_AES256;
- keylen = 32;
- }
- else
- {
- err = gpg_error (GPG_ERR_PUBKEY_ALGO);
- goto leave;
- }
-
- err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str,
- secret, secretlen, ukm, ukmlen);
- if (err)
- goto leave;
-
- if (DBG_CRYPTO)
- log_printhex (key, keylen, "KEK .....:");
-
- /* Unwrap the key. */
- if ((datalen % 8) || datalen < 16)
- {
- log_error ("can't use a shared secret of %u bytes for ecdh\n", datalen);
- err = gpg_error (GPG_ERR_BAD_DATA);
- goto leave;
- }
-
- resultlen = datalen - 8;
- result = xtrymalloc_secure (resultlen);
- if (!result)
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
-
- err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
- if (err)
- {
- log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- err = gcry_cipher_setkey (cipher_hd, key, keylen);
- wipememory (key, sizeof key);
- if (err)
- {
- log_error ("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (err));
- goto leave;
- }
-
- err = gcry_cipher_decrypt (cipher_hd, result, resultlen, data, datalen);
- if (err)
- {
- log_error ("ecdh failed in gcry_cipher_decrypt: %s\n",gpg_strerror (err));
- goto leave;
- }
-
- *r_resultlen = resultlen;
- *r_result = result;
- result = NULL;
-
- leave:
- if (result)
- {
- wipememory (result, resultlen);
- xfree (result);
- }
- gcry_cipher_close (cipher_hd);
- xfree (encr_algo_str);
- xfree (wrap_algo_str);
- xfree (ioarray[0].data);
- xfree (ioarray[1].data);
- xfree (ioarray[2].data);
- xfree (ioarray[3].data);
- return err;
-}
-
-
/* Helper for pwri_decrypt to parse the derive info.
* Example data for (DER,DERLEN):
* SEQUENCE {
* OCTET STRING
* 60 76 4B E9 5E DF 3C F8 B2 F9 B6 C2 7D 5A FB 90
* 23 B6 47 DF
* INTEGER 10000
* SEQUENCE {
* OBJECT IDENTIFIER
* hmacWithSHA512 (1 2 840 113549 2 11)
* NULL
* }
* }
*/
static gpg_error_t
pwri_parse_pbkdf2 (const unsigned char *der, size_t derlen,
unsigned char const **r_salt, unsigned int *r_saltlen,
unsigned long *r_iterations,
int *r_digest)
{
gpg_error_t err;
size_t objlen, hdrlen;
int class, tag, constructed, ndef;
char *oidstr;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_SEQUENCE
|| !constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
derlen = objlen;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_OCTET_STRING
|| constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
*r_salt = der;
*r_saltlen = objlen;
der += objlen;
derlen -= objlen;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_INTEGER
|| constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
*r_iterations = 0;
for (; objlen; objlen--)
{
*r_iterations <<= 8;
*r_iterations |= (*der++) & 0xff;
derlen--;
}
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_SEQUENCE
|| !constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
derlen = objlen;
err = parse_ber_header (&der, &derlen, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (!err && (objlen > derlen || tag != TAG_OBJECT_ID
|| constructed || ndef))
err = gpg_error (GPG_ERR_INV_OBJ);
if (err)
return err;
oidstr = ksba_oid_to_str (der, objlen);
if (!oidstr)
return gpg_error_from_syserror ();
*r_digest = gcry_md_map_name (oidstr);
if (*r_digest)
;
else if (!strcmp (oidstr, "1.2.840.113549.2.7"))
*r_digest = GCRY_MD_SHA1;
else if (!strcmp (oidstr, "1.2.840.113549.2.8"))
*r_digest = GCRY_MD_SHA224;
else if (!strcmp (oidstr, "1.2.840.113549.2.9"))
*r_digest = GCRY_MD_SHA256;
else if (!strcmp (oidstr, "1.2.840.113549.2.10"))
*r_digest = GCRY_MD_SHA384;
else if (!strcmp (oidstr, "1.2.840.113549.2.11"))
*r_digest = GCRY_MD_SHA512;
else
err = gpg_error (GPG_ERR_DIGEST_ALGO);
ksba_free (oidstr);
return err;
}
/* Password based decryption.
* ENC_VAL has the form:
* (enc-val
* (pwri
* (derive-algo <oid>) --| both are optional
* (derive-parm <der>) --|
* (encr-algo <oid>)
* (encr-parm <iv>)
* (encr-key <key>))) -- this is the encrypted session key
*
*/
static gpg_error_t
pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val,
unsigned char **r_result, unsigned int *r_resultlen,
struct decrypt_filter_parm_s *parm)
{
gpg_error_t err;
gcry_buffer_t ioarray[5] = { {0} };
char *derive_algo_str = NULL;
char *encr_algo_str = NULL;
const unsigned char *dparm; /* Alias for ioarray[1]. */
unsigned int dparmlen;
const unsigned char *eparm; /* Alias for ioarray[3]. */
unsigned int eparmlen;
const unsigned char *ekey; /* Alias for ioarray[4]. */
unsigned int ekeylen;
unsigned char kek[32];
unsigned int keklen;
int encr_algo;
enum gcry_cipher_modes encr_mode;
gcry_cipher_hd_t encr_hd = NULL;
unsigned char *result = NULL;
unsigned int resultlen;
unsigned int blklen;
const unsigned char *salt; /* Points int dparm. */
unsigned int saltlen;
unsigned long iterations;
int digest_algo;
char *passphrase = NULL;
*r_resultlen = 0;
*r_result = NULL;
err = gcry_sexp_extract_param (enc_val, "enc-val!pwri",
"&'derive-algo'?'derive-parm'?"
"'encr-algo''encr-parm''encr-key'",
ioarray+0, ioarray+1,
ioarray+2, ioarray+3, ioarray+4, NULL);
if (err)
{
/* If this is not pwri element, it is likely a kekri element
* which we do not yet support. Change the error back to the
* original as returned by ksba_cms_get_issuer. */
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
err = gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
else
log_error ("extracting PWRI parameter failed: %s\n",
gpg_strerror (err));
goto leave;
}
if (ioarray[0].data)
{
derive_algo_str = string_from_gcry_buffer (ioarray+0);
if (!derive_algo_str)
{
err = gpg_error_from_syserror ();
goto leave;
}
}
dparm = ioarray[1].data;
dparmlen = ioarray[1].len;
encr_algo_str = string_from_gcry_buffer (ioarray+2);
if (!encr_algo_str)
{
err = gpg_error_from_syserror ();
goto leave;
}
eparm = ioarray[3].data;
eparmlen = ioarray[3].len;
ekey = ioarray[4].data;
ekeylen = ioarray[4].len;
/* Check parameters. */
if (DBG_CRYPTO)
{
if (derive_algo_str)
{
log_debug ("derive algo: %s\n", derive_algo_str);
log_printhex (dparm, dparmlen, "derive parm:");
}
log_debug ("encr algo .: %s\n", encr_algo_str);
log_printhex (eparm, eparmlen, "encr parm .:");
log_printhex (ekey, ekeylen, "encr key .:");
}
if (!derive_algo_str)
{
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
log_info ("PWRI with no key derivation detected\n");
goto leave;
}
if (strcmp (derive_algo_str, "1.2.840.113549.1.5.12"))
{
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
log_info ("PWRI does not use PBKDF2 (but %s)\n", derive_algo_str);
goto leave;
}
digest_algo = 0; /*(silence cc warning)*/
err = pwri_parse_pbkdf2 (dparm, dparmlen,
&salt, &saltlen, &iterations, &digest_algo);
if (err)
{
log_error ("parsing PWRI parameter failed: %s\n", gpg_strerror (err));
goto leave;
}
parm->is_de_vs = (parm->is_de_vs
&& gnupg_digest_is_compliant (CO_DE_VS, digest_algo));
encr_algo = gcry_cipher_map_name (encr_algo_str);
encr_mode = gcry_cipher_mode_from_oid (encr_algo_str);
if (!encr_algo || !encr_mode)
{
log_error ("PWRI uses unknown algorithm %s\n", encr_algo_str);
err = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
parm->is_de_vs =
(parm->is_de_vs
&& gnupg_cipher_is_compliant (CO_DE_VS, encr_algo, encr_mode));
keklen = gcry_cipher_get_algo_keylen (encr_algo);
blklen = gcry_cipher_get_algo_blklen (encr_algo);
if (!keklen || keklen > sizeof kek || blklen != 16 )
{
log_error ("PWRI algorithm %s cannot be used\n", encr_algo_str);
err = gpg_error (GPG_ERR_INV_KEYLEN);
goto leave;
}
if ((ekeylen % blklen) || (ekeylen / blklen < 2))
{
/* Note that we need at least two full blocks. */
log_error ("PWRI uses a wrong length of encrypted key\n");
err = gpg_error (GPG_ERR_INV_KEYLEN);
goto leave;
}
err = gpgsm_agent_ask_passphrase
(ctrl,
i18n_utf8 (N_("Please enter the passphrase for decryption.")),
0, &passphrase);
if (err)
goto leave;
err = gcry_kdf_derive (passphrase, strlen (passphrase),
GCRY_KDF_PBKDF2, digest_algo,
salt, saltlen, iterations,
keklen, kek);
if (passphrase)
{
wipememory (passphrase, strlen (passphrase));
xfree (passphrase);
passphrase = NULL;
}
if (err)
{
log_error ("deriving key from passphrase failed: %s\n",
gpg_strerror (err));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (kek, keklen, "KEK .......:");
/* Unwrap the key. */
resultlen = ekeylen;
result = xtrymalloc_secure (resultlen);
if (!result)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = gcry_cipher_open (&encr_hd, encr_algo, encr_mode, 0);
if (err)
{
log_error ("PWRI failed to open cipher: %s\n", gpg_strerror (err));
goto leave;
}
err = gcry_cipher_setkey (encr_hd, kek, keklen);
wipememory (kek, sizeof kek);
if (!err)
err = gcry_cipher_setiv (encr_hd, ekey + ekeylen - 2 * blklen, blklen);
if (!err)
err = gcry_cipher_decrypt (encr_hd, result + ekeylen - blklen, blklen,
ekey + ekeylen - blklen, blklen);
if (!err)
err = gcry_cipher_setiv (encr_hd, result + ekeylen - blklen, blklen);
if (!err)
err = gcry_cipher_decrypt (encr_hd, result, ekeylen - blklen,
ekey, ekeylen - blklen);
/* (We assume that that eparm is the octet string with the IV) */
if (!err)
err = gcry_cipher_setiv (encr_hd, eparm, eparmlen);
if (!err)
err = gcry_cipher_decrypt (encr_hd, result, resultlen, NULL, 0);
if (err)
{
log_error ("KEK decryption failed for PWRI: %s\n", gpg_strerror (err));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (result, resultlen, "Frame .....:");
if (result[0] < 8 /* At least 64 bits */
|| (result[0] % 8) /* Multiple of 64 bits */
|| result[0] > resultlen - 4 /* Not more than the size of the input */
|| ( (result[1] ^ result[4]) /* Matching check bytes. */
& (result[2] ^ result[5])
& (result[3] ^ result[6]) ) != 0xff)
{
err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
goto leave;
}
*r_resultlen = result[0];
*r_result = memmove (result, result + 4, result[0]);
result = NULL;
leave:
if (result)
{
wipememory (result, resultlen);
xfree (result);
}
if (passphrase)
{
wipememory (passphrase, strlen (passphrase));
xfree (passphrase);
}
gcry_cipher_close (encr_hd);
xfree (derive_algo_str);
xfree (encr_algo_str);
xfree (ioarray[0].data);
xfree (ioarray[1].data);
xfree (ioarray[2].data);
xfree (ioarray[3].data);
xfree (ioarray[4].data);
return err;
}
+static int
+determine_wrap_cipher (unsigned char *wrap_algo, size_t len)
+{
+ int cipher = -1;
+
+ if (len == 22 && !memcmp (wrap_algo, "2.16.840.1.101.3.4.1.5", len))
+ cipher = GCRY_CIPHER_AES128;
+ else if (len == 23 && !memcmp (wrap_algo, "2.16.840.1.101.3.4.1.25", len))
+ cipher = GCRY_CIPHER_AES192;
+ else if (len == 23 && !memcmp (wrap_algo, "2.16.840.1.101.3.4.1.45", len))
+ cipher = GCRY_CIPHER_AES256;
+
+ return cipher;
+}
+
+static int
+determine_hashalgo (unsigned char *kdf_scheme, size_t len)
+{
+ int hashalgo = -1;
+
+ if (len == 14 && !memcmp (kdf_scheme, "1.3.132.1.11.1", len))
+ {
+ /* dhSinglePass-stdDH-sha256kdf-scheme */
+ hashalgo = GCRY_MD_SHA256;
+ }
+ else if (len == 14 && !memcmp (kdf_scheme, "1.3.132.1.11.2", len))
+ {
+ /* dhSinglePass-stdDH-sha384kdf-scheme */
+ hashalgo = GCRY_MD_SHA384;
+ }
+ else if (len == 14 && !memcmp (kdf_scheme, "1.3.132.1.11.3", len))
+ {
+ /* dhSinglePass-stdDH-sha512kdf-scheme */
+ hashalgo = GCRY_MD_SHA512;
+ }
+ else if (len == 21 && !memcmp (kdf_scheme, "1.3.133.16.840.63.0.2", len))
+ {
+ /* dhSinglePass-stdDH-sha1kdf-scheme */
+ hashalgo = GCRY_MD_SHA1;
+ }
+
+ return hashalgo;
+}
+
+
+/* Construct the
+ * ECC-CMS-SharedInfo ::= SEQUENCE {
+ * keyInfo AlgorithmIdentifier,
+ * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL,
+ * suppPubInfo [2] EXPLICIT OCTET STRING }
+ * as described in RFC-5753, 7.2. */
+static gpg_error_t
+build_shared_info (size_t kek_len, const char *wrap_algo_str,
+ const void *ukm, size_t ukm_len,
+ unsigned char **result_p, size_t *len_p)
+{
+ gpg_error_t err;
+ unsigned char *oid;
+ size_t oidlen, toidlen, tkeyinfo, tukmlen, tsupppubinfo;
+ unsigned char keylenbuf[6];
+ membuf_t mb = MEMBUF_ZERO;
+
+ err = ksba_oid_from_str (wrap_algo_str, &oid, &oidlen);
+ if (err)
+ return err;
+
+ toidlen = get_tlv_length (CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen);
+ tkeyinfo = get_tlv_length (CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen);
+
+ tukmlen = ukm? get_tlv_length (CLASS_CONTEXT, 0, 1, ukm_len) : 0;
+
+ kek_len *= 8;
+ keylenbuf[0] = TAG_OCTET_STRING;
+ keylenbuf[1] = 4;
+ keylenbuf[2] = (kek_len >> 24);
+ keylenbuf[3] = (kek_len >> 16);
+ keylenbuf[4] = (kek_len >> 8);
+ keylenbuf[5] = kek_len;
+
+ tsupppubinfo = get_tlv_length (CLASS_CONTEXT, 2, 1, sizeof keylenbuf);
+
+ put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1,
+ tkeyinfo + tukmlen + tsupppubinfo);
+ put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1,
+ toidlen);
+ put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen);
+ put_membuf (&mb, oid, oidlen);
+ ksba_free (oid);
+
+ if (ukm)
+ {
+ put_tlv_to_membuf (&mb, CLASS_CONTEXT, 0, 1, ukm_len);
+ put_membuf (&mb, ukm, ukm_len);
+ }
+
+ put_tlv_to_membuf (&mb, CLASS_CONTEXT, 2, 1, sizeof keylenbuf);
+ put_membuf (&mb, keylenbuf, sizeof keylenbuf);
+
+ if (!(*result_p = get_membuf (&mb, len_p)))
+ err = gpg_error_from_syserror ();
+
+ return err;
+}
+
+
+/* Maximum buffer length of encrypted session key in S-Expressoin for
+ * KEM API. */
+#define ENC_VAL_KEM_MAX 1024
+
+/* For ECC, get the session key by asking gpg-agent with KEM API. */
+static gpg_error_t
+ecc_kem_pkdecrypt (ctrl_t ctrl, const char *hexkeygrip,
+ const char *desc, ksba_const_sexp_t enc_val,
+ char **r_seskey, size_t *r_seskeylen)
+{
+ gcry_error_t err;
+ gcry_sexp_t s_enc_val;
+
+ gcry_mpi_t encrypted_sessionkey_mpi = NULL;
+ gcry_mpi_t ecc_ct_mpi = NULL;
+ gcry_buffer_t encr_algo = { 0, 0, 0, NULL };
+ gcry_buffer_t wrap_algo = { 0, 0, 0, NULL };
+ gcry_buffer_t ukm = { 0, 0, 0, NULL };
+
+ int keywrap_cipher_algo, kdf_hash_algo;
+ size_t kek_len;
+
+ char *wrap_algo_str;
+
+ unsigned char *kdf_params = NULL;
+ size_t kdf_params_len;
+
+ gcry_sexp_t s_enc_val_kem;
+ unsigned char enc_val_kem[ENC_VAL_KEM_MAX];
+ size_t enc_val_kem_len;
+
+ /*
+ * Firstly, from ENC_VAL, extract hash algo for KDF, key
+ * wrap cipher algo, cipher text, wrapped session key, and
+ * optional user key material (UKM).
+ */
+ err = gcry_sexp_sscan (&s_enc_val, NULL, enc_val,
+ gcry_sexp_canon_len (enc_val, 0, NULL, NULL));
+ if (err)
+ goto leave;
+
+ err = gcry_sexp_extract_param (s_enc_val, "enc-val",
+ "/es&'encr-algo''wrap-algo''ukm'?",
+ &ecc_ct_mpi, &encrypted_sessionkey_mpi,
+ &encr_algo, &wrap_algo, &ukm, NULL);
+ gcry_sexp_release (s_enc_val);
+ if (err)
+ {
+ if (opt.verbose)
+ log_info ("%s: extracting parameters failed\n", __func__);
+ goto leave;
+ }
+
+ if (!encr_algo.data || !wrap_algo.data)
+ {
+ if (opt.verbose)
+ log_info ("%s: the KDF parameters are required\n", __func__);
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ kdf_hash_algo = determine_hashalgo (encr_algo.data, encr_algo.size);
+ if (kdf_hash_algo < 0)
+ {
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto leave;
+ }
+
+ keywrap_cipher_algo = determine_wrap_cipher (wrap_algo.data,
+ wrap_algo.size);
+ if (keywrap_cipher_algo < 0)
+ {
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto leave;
+ }
+
+ kek_len = gcry_cipher_get_algo_keylen (keywrap_cipher_algo);
+ if (kek_len == 0)
+ {
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ wrap_algo_str = string_from_gcry_buffer (&wrap_algo);
+ if (!wrap_algo_str)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Secondly, compose the KDF parameters. */
+ err = build_shared_info (kek_len, wrap_algo_str, ukm.data, ukm.size,
+ &kdf_params, &kdf_params_len);
+ xfree (wrap_algo_str);
+ if (err)
+ goto leave;
+
+ /* Thirdly, build the S-EXP for the request to the gpg-agent. */
+ err = gcry_sexp_build (&s_enc_val_kem, NULL,
+ "(enc-val(ecc(c%d)(h%d)(e%m)(s%m)(kdf-params%b)))",
+ keywrap_cipher_algo, kdf_hash_algo,
+ ecc_ct_mpi, encrypted_sessionkey_mpi,
+ (int)kdf_params_len, kdf_params);
+ if (err)
+ goto leave;
+
+ enc_val_kem_len = gcry_sexp_sprint (s_enc_val_kem, GCRYSEXP_FMT_CANON,
+ enc_val_kem, ENC_VAL_KEM_MAX);
+ gcry_sexp_release (s_enc_val_kem);
+ if (!enc_val_kem_len)
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ goto leave;
+ }
+
+ /* Ask gpg-agent to decrypt the wrapped session key with KEM API. */
+ err = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, 1, enc_val_kem,
+ r_seskey, r_seskeylen);
+ leave:
+ xfree (kdf_params);
+ gcry_free (encr_algo.data);
+ gcry_free (wrap_algo.data);
+ gcry_free (ukm.data);
+ gcry_mpi_release (ecc_ct_mpi);
+ gcry_mpi_release (encrypted_sessionkey_mpi);
+ return err;
+}
+
/* Decrypt the session key and fill in the parm structure. The
algo and the IV is expected to be already in PARM. */
static int
prepare_decryption (ctrl_t ctrl, const char *hexkeygrip,
- int pk_algo, unsigned int nbits, const char *desc,
+ int pk_algo, const char *desc,
ksba_const_sexp_t enc_val,
struct decrypt_filter_parm_s *parm)
{
char *seskey = NULL;
size_t n, seskeylen;
int pwri = !hexkeygrip && !pk_algo;
int rc;
if (DBG_CRYPTO)
log_printcanon ("decrypting:", enc_val, 0);
if (pwri) /* Password based encryption. */
{
gcry_sexp_t s_enc_val;
unsigned char *decrypted;
unsigned int decryptedlen;
rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val,
gcry_sexp_canon_len (enc_val, 0, NULL, NULL));
if (rc)
goto leave;
rc = pwri_decrypt (ctrl, s_enc_val, &decrypted, &decryptedlen, parm);
gcry_sexp_release (s_enc_val);
if (rc)
goto leave;
xfree (seskey);
seskey = decrypted;
seskeylen = decryptedlen;
n = 0;
}
else
{
- rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val,
- &seskey, &seskeylen);
+ if (pk_algo == GCRY_PK_ECC)
+ rc = ecc_kem_pkdecrypt (ctrl, hexkeygrip, desc, enc_val,
+ &seskey, &seskeylen);
+ else
+ rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, 0, enc_val,
+ &seskey, &seskeylen);
+
if (rc)
{
log_error ("error decrypting session key: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (seskey, seskeylen, "DEK frame:");
n = 0;
if (pk_algo == GCRY_PK_ECC)
{
- gcry_sexp_t s_enc_val;
- unsigned char *decrypted;
- unsigned int decryptedlen;
-
- rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val,
- gcry_sexp_canon_len (enc_val, 0, NULL, NULL));
- if (rc)
- goto leave;
-
- rc = ecdh_decrypt (seskey, seskeylen, nbits, s_enc_val,
- &decrypted, &decryptedlen);
- gcry_sexp_release (s_enc_val);
- if (rc)
- goto leave;
- xfree (seskey);
- seskey = decrypted;
- seskeylen = decryptedlen;
+ /* When it's ECC, no more checks are needed. It's done in
+ * the agent side. */
}
else if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16)
{
/* Smells like an AES-128, 3-DES, or AES-256 key. This might
* happen because a SC has already done the unpacking. A better
* solution would be to test for this only after we triggered
* the GPG_ERR_INV_SESSION_KEY. */
}
else
{
if (n + 7 > seskeylen )
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
/* FIXME: Actually the leading zero is required but due to the way
we encode the output in libgcrypt as an MPI we are not able to
encode that leading zero. However, when using a Smartcard we are
doing it the right way and therefore we have to skip the zero. This
should be fixed in gpg-agent of course. */
if (!seskey[n])
n++;
if (seskey[n] != 2 ) /* Wrong block type version. */
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */
;
n++; /* and the zero byte */
if (n >= seskeylen )
{
rc = gpg_error (GPG_ERR_INV_SESSION_KEY);
goto leave;
}
}
}
if (DBG_CRYPTO)
{
log_printhex (seskey+n, seskeylen-n, "CEK .......:");
log_printhex (parm->iv, parm->ivlen, "IV ........:");
}
if (opt.verbose)
log_info (_("%s.%s encrypted data\n"),
gcry_cipher_algo_name (parm->algo),
cipher_mode_to_string (parm->mode));
rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0);
if (rc)
{
log_error ("error creating decryptor: %s\n", gpg_strerror (rc));
goto leave;
}
rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n);
if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
{
log_info (_("WARNING: message was encrypted with "
"a weak key in the symmetric cipher.\n"));
rc = 0;
}
if (rc)
{
log_error("key setup failed: %s\n", gpg_strerror(rc) );
goto leave;
}
rc = gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen);
if (rc)
{
log_error("IV setup failed: %s\n", gpg_strerror(rc) );
goto leave;
}
if (parm->mode == GCRY_CIPHER_MODE_GCM)
{
/* GCM mode really sucks in CMS. We need to know the AAD before
* we start decrypting but CMS puts the AAD after the content.
* Thus temporary files are required. Let's hope that no real
* messages with actual AAD are ever used. OCB Rules! */
}
leave:
xfree (seskey);
return rc;
}
/* This function is called by the KSBA writer just before the actual
write is done. The function must take INLEN bytes from INBUF,
decrypt it and store it inoutbuf which has a maximum size of
maxoutlen. The valid bytes in outbuf should be return in outlen.
Due to different buffer sizes or different length of input and
output, it may happen that fewer bytes are processed or fewer bytes
are written. */
static gpg_error_t
decrypt_filter (void *arg,
const void *inbuf, size_t inlen, size_t *inused,
void *outbuf, size_t maxoutlen, size_t *outlen)
{
struct decrypt_filter_parm_s *parm = arg;
int blklen = parm->blklen;
size_t orig_inlen = inlen;
/* fixme: Should we issue an error when we have not seen one full block? */
if (!inlen)
return gpg_error (GPG_ERR_BUG);
if (maxoutlen < 2*parm->blklen)
return gpg_error (GPG_ERR_BUG);
/* Make some space because we will later need an extra block at the end. */
maxoutlen -= blklen;
if (parm->helpblocklen)
{
int i, j;
for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++)
parm->helpblock[i] = ((const char*)inbuf)[j];
inlen -= j;
if (blklen > maxoutlen)
return gpg_error (GPG_ERR_BUG);
if (i < blklen)
{
parm->helpblocklen = i;
*outlen = 0;
}
else
{
parm->helpblocklen = 0;
if (parm->any_data)
{
memcpy (outbuf, parm->lastblock, blklen);
*outlen =blklen;
}
else
*outlen = 0;
gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen,
parm->helpblock, blklen);
parm->any_data = 1;
}
*inused = orig_inlen - inlen;
return 0;
}
if (inlen > maxoutlen)
inlen = maxoutlen;
if (inlen % blklen)
{ /* store the remainder away */
parm->helpblocklen = inlen%blklen;
inlen = inlen/blklen*blklen;
memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen);
}
*inused = inlen + parm->helpblocklen;
if (inlen)
{
log_assert (inlen >= blklen);
if (parm->any_data)
{
gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen,
inbuf, inlen);
memcpy (outbuf, parm->lastblock, blklen);
memcpy (parm->lastblock,(char*)outbuf+inlen, blklen);
*outlen = inlen;
}
else
{
gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen);
*outlen = inlen - blklen;
parm->any_data = 1;
}
}
else
*outlen = 0;
return 0;
}
/* This is the GCM version of decrypt_filter. */
static gpg_error_t
decrypt_gcm_filter (void *arg,
const void *inbuf, size_t inlen, size_t *inused,
void *outbuf, size_t maxoutlen, size_t *outlen)
{
struct decrypt_filter_parm_s *parm = arg;
if (!inlen)
return gpg_error (GPG_ERR_BUG);
if (maxoutlen < parm->blklen)
return gpg_error (GPG_ERR_BUG);
if (inlen > maxoutlen)
inlen = maxoutlen;
*inused = inlen;
if (inlen)
{
gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
*outlen = inlen;
parm->any_data = 1;
}
else
*outlen = 0;
return 0;
}
/* Perform a decrypt operation. */
int
gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp)
{
int rc;
gnupg_ksba_io_t b64reader = NULL;
gnupg_ksba_io_t b64writer = NULL;
ksba_reader_t reader;
ksba_writer_t writer;
ksba_cms_t cms = NULL;
ksba_stop_reason_t stopreason;
KEYDB_HANDLE kh;
int recp;
struct decrypt_filter_parm_s dfparm;
char *curve = NULL;
memset (&dfparm, 0, sizeof dfparm);
audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT);
kh = keydb_new (ctrl);
if (!kh)
{
log_error (_("failed to allocate keyDB handle\n"));
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
rc = gnupg_ksba_create_reader
(&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0)
| (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0)
| (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)),
in_fp, &reader);
if (rc)
{
log_error ("can't create reader: %s\n", gpg_strerror (rc));
goto leave;
}
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));
goto leave;
}
gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl);
if (ctrl->input_size_hint)
gnupg_ksba_set_total (b64writer, ctrl->input_size_hint);
rc = ksba_cms_new (&cms);
if (rc)
goto leave;
rc = ksba_cms_set_reader_writer (cms, reader, writer);
if (rc)
{
log_error ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (rc));
goto leave;
}
audit_log (ctrl->audit, AUDIT_SETUP_READY);
/* Parser loop. */
do
{
rc = ksba_cms_parse (cms, &stopreason);
if (rc)
{
log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
goto leave;
}
if (stopreason == KSBA_SR_BEGIN_DATA
|| stopreason == KSBA_SR_DETACHED_DATA)
{
int algo, mode;
const char *algoid;
int any_key = 0;
audit_log (ctrl->audit, AUDIT_GOT_DATA);
algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/);
algo = gcry_cipher_map_name (algoid);
mode = gcry_cipher_mode_from_oid (algoid);
if (!algo || !mode)
{
rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
log_error ("unsupported algorithm '%s'\n", algoid? algoid:"?");
if (algoid && !strcmp (algoid, "1.2.840.113549.3.2"))
log_info (_("(this is the RC2 algorithm)\n"));
else if (!algoid)
log_info (_("(this does not seem to be an encrypted"
" message)\n"));
{
char numbuf[50];
sprintf (numbuf, "%d", rc);
gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm",
numbuf, algoid?algoid:"?", NULL);
audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid);
}
/* If it seems that this is not an encrypted message we
return a more sensible error code. */
if (!algoid)
rc = gpg_error (GPG_ERR_NO_DATA);
goto leave;
}
/* Check compliance. */
if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode))
{
log_error (_("cipher algorithm '%s'"
" may not be used in %s mode\n"),
gcry_cipher_algo_name (algo),
gnupg_compliance_option_string (opt.compliance));
rc = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
/* For CMS, CO_DE_VS demands CBC mode. */
dfparm.is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode);
audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo);
dfparm.algo = algo;
dfparm.mode = mode;
dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
if (dfparm.blklen > sizeof (dfparm.helpblock))
{
rc = gpg_error (GPG_ERR_BUG);
goto leave;
}
rc = ksba_cms_get_content_enc_iv (cms,
dfparm.iv,
sizeof (dfparm.iv),
&dfparm.ivlen);
if (rc)
{
log_error ("error getting IV: %s\n", gpg_strerror (rc));
goto leave;
}
for (recp=0; !any_key; recp++)
{
char *issuer;
ksba_sexp_t serial;
ksba_sexp_t enc_val;
char *hexkeygrip = NULL;
char *pkalgostr = NULL;
char *pkfpr = NULL;
char *desc = NULL;
char kidbuf[16+1];
int tmp_rc;
ksba_cert_t cert = NULL;
unsigned int nbits;
int pk_algo = 0;
int maybe_pwri = 0;
*kidbuf = 0;
tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial);
if (tmp_rc == -1 && recp)
break; /* no more recipients */
audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
if (gpg_err_code (tmp_rc) == GPG_ERR_UNSUPPORTED_CMS_OBJ)
{
maybe_pwri = 1;
}
else if (tmp_rc)
{
log_error ("recp %d - error getting info: %s\n",
recp, gpg_strerror (tmp_rc));
}
else
{
if (opt.verbose)
{
log_info ("recp %d - issuer: '%s'\n",
recp, issuer? issuer:"[NONE]");
log_info ("recp %d - serial: ", recp);
gpgsm_dump_serial (serial);
log_printf ("\n");
}
if (ctrl->audit)
{
char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
xfree (tmpstr);
}
keydb_search_reset (kh);
rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial);
if (rc)
{
log_error ("failed to find the certificate: %s\n",
gpg_strerror(rc));
goto oops;
}
rc = keydb_get_cert (kh, &cert);
if (rc)
{
log_error ("failed to get cert: %s\n", gpg_strerror (rc));
goto oops;
}
/* Print the ENC_TO status line. Note that we can
do so only if we have the certificate. This is
in contrast to gpg where the keyID is commonly
included in the encrypted messages. It is too
cumbersome to retrieve the used algorithm, thus
we don't print it for now. We also record the
keyid for later use. */
{
unsigned long kid[2];
kid[0] = gpgsm_get_short_fingerprint (cert, kid+1);
snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX",
kid[1], kid[0]);
gpgsm_status2 (ctrl, STATUS_ENC_TO,
kidbuf, "0", "0", NULL);
}
/* Put the certificate into the audit log. */
audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0);
/* Just in case there is a problem with the own
certificate we print this message - should never
happen of course */
rc = gpgsm_cert_use_decrypt_p (cert);
if (rc)
{
char numbuf[50];
sprintf (numbuf, "%d", rc);
gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage",
numbuf, NULL);
rc = 0;
}
hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
desc = gpgsm_format_keydesc (cert);
pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
pkalgostr = gpgsm_pubkey_algo_string (cert, NULL);
xfree (curve);
pk_algo = gpgsm_get_key_algo_info (cert, &nbits, &curve);
if (!opt.quiet)
log_info (_("encrypted to %s key %s\n"), pkalgostr, pkfpr);
/* Check compliance. */
if (!gnupg_pk_is_allowed (opt.compliance,
PK_USE_DECRYPTION,
pk_algo, PK_ALGO_FLAG_ECC18,
NULL, nbits, curve))
{
char kidstr[10+1];
snprintf (kidstr, sizeof kidstr, "0x%08lX",
gpgsm_get_short_fingerprint (cert, NULL));
log_info (_("key %s is not suitable for decryption"
" in %s mode\n"),
kidstr,
gnupg_compliance_option_string(opt.compliance));
rc = gpg_error (GPG_ERR_PUBKEY_ALGO);
goto oops;
}
/* Check that all certs are compliant with CO_DE_VS. */
dfparm.is_de_vs =
(dfparm.is_de_vs
&& gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0,
NULL, nbits, curve));
oops:
if (rc)
{
/* We cannot check compliance of certs that we
* don't have. */
dfparm.is_de_vs = 0;
}
xfree (issuer);
xfree (serial);
ksba_cert_release (cert);
}
if ((!hexkeygrip || !pk_algo) && !maybe_pwri)
;
else if (!(enc_val = ksba_cms_get_enc_val (cms, recp)))
{
log_error ("recp %d - error getting encrypted session key\n",
recp);
if (maybe_pwri)
log_info ("(possibly unsupported KEK info)\n");
}
else
{
if (maybe_pwri && opt.verbose)
log_info ("recp %d - KEKRI or PWRI\n", recp);
- rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits,
+ rc = prepare_decryption (ctrl, hexkeygrip, pk_algo,
desc, enc_val, &dfparm);
xfree (enc_val);
if (rc)
{
log_info ("decrypting session key failed: %s\n",
gpg_strerror (rc));
if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf)
gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL);
}
else
{ /* setup the bulk decrypter */
any_key = 1;
ksba_writer_set_filter
(writer,
dfparm.mode == GCRY_CIPHER_MODE_GCM?
decrypt_gcm_filter : decrypt_filter,
&dfparm);
if (dfparm.is_de_vs
&& gnupg_gcrypt_is_compliant (CO_DE_VS))
gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE,
gnupg_status_compliance_flag (CO_DE_VS));
else if (opt.require_compliance
&& opt.compliance == CO_DE_VS)
{
log_error (_("operation forced to fail due to"
" unfulfilled compliance rules\n"));
gpgsm_errors_seen = 1;
}
}
audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc);
}
xfree (pkalgostr);
xfree (pkfpr);
xfree (hexkeygrip);
xfree (desc);
}
/* If we write an audit log add the unused recipients to the
log as well. */
if (ctrl->audit && any_key)
{
for (;; recp++)
{
char *issuer;
ksba_sexp_t serial;
int tmp_rc;
tmp_rc = ksba_cms_get_issuer_serial (cms, recp,
&issuer, &serial);
if (tmp_rc == -1)
break; /* no more recipients */
audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp);
if (tmp_rc)
log_error ("recp %d - error getting info: %s\n",
recp, gpg_strerror (tmp_rc));
else
{
char *tmpstr = gpgsm_format_sn_issuer (serial, issuer);
audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr);
xfree (tmpstr);
xfree (issuer);
xfree (serial);
}
}
}
if (!any_key)
{
if (!rc)
rc = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
}
else if (stopreason == KSBA_SR_END_DATA)
{
ksba_writer_set_filter (writer, NULL, NULL);
if (dfparm.mode == GCRY_CIPHER_MODE_GCM)
{
/* Nothing yet to do. We wait for the ready event. */
}
else if (dfparm.any_data )
{ /* write the last block with padding removed */
int i, npadding = dfparm.lastblock[dfparm.blklen-1];
if (!npadding || npadding > dfparm.blklen)
{
log_error ("invalid padding with value %d\n", npadding);
rc = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
rc = ksba_writer_write (writer,
dfparm.lastblock,
dfparm.blklen - npadding);
if (rc)
goto leave;
for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++)
{
if (dfparm.lastblock[i] != npadding)
{
log_error ("inconsistent padding\n");
rc = gpg_error (GPG_ERR_INV_DATA);
goto leave;
}
}
}
}
else if (stopreason == KSBA_SR_READY)
{
if (dfparm.mode == GCRY_CIPHER_MODE_GCM)
{
char *authtag;
size_t authtaglen;
rc = ksba_cms_get_message_digest (cms, 0, &authtag, &authtaglen);
if (rc)
{
log_error ("error getting authtag: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (authtag, authtaglen, "Authtag ...:");
rc = gcry_cipher_checktag (dfparm.hd, authtag, authtaglen);
xfree (authtag);
if (rc)
log_error ("data is not authentic: %s\n", gpg_strerror (rc));
goto leave;
}
}
}
while (stopreason != KSBA_SR_READY);
rc = gnupg_ksba_finish_writer (b64writer);
if (rc)
{
log_error ("write failed: %s\n", gpg_strerror (rc));
goto leave;
}
gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL);
leave:
audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc);
if (rc)
{
gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL);
log_error ("message decryption failed: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (rc));
}
xfree (curve);
ksba_cms_release (cms);
gnupg_ksba_destroy_reader (b64reader);
gnupg_ksba_destroy_writer (b64writer);
keydb_release (kh);
if (dfparm.hd)
gcry_cipher_close (dfparm.hd);
return rc;
}
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index cc049d05b..5f4f9a24a 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -1,594 +1,594 @@
/* gpgsm.h - Global definitions for GpgSM
* Copyright (C) 2001, 2003, 2004, 2007, 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 <https://www.gnu.org/licenses/>.
*/
#ifndef GPGSM_H
#define GPGSM_H
#ifdef GPG_ERR_SOURCE_DEFAULT
#error GPG_ERR_SOURCE_DEFAULT already defined
#endif
#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGSM
#include <gpg-error.h>
#include <ksba.h>
#include "../common/util.h"
#include "../common/status.h"
#include "../common/audit.h"
#include "../common/session-env.h"
#include "../common/ksba-io-support.h"
#include "../common/compliance.h"
/* The maximum length of a binary fingerprints. This is used to
* provide a static buffer and will be increased if we need to support
* longer fingerprints. */
#define MAX_FINGERPRINT_LEN 32
/* The maximum length of a binary digest. */
#define MAX_DIGEST_LEN 64 /* Fits for SHA-512 */
/* A large struct named "opt" to keep global flags. */
EXTERN_UNLESS_MAIN_MODULE
struct
{
unsigned int debug; /* debug flags (DBG_foo_VALUE) */
int verbose; /* verbosity level */
int quiet; /* be as quiet as possible */
int batch; /* run in batch mode, i.e w/o any user interaction */
int answer_yes; /* assume yes on most questions */
int answer_no; /* assume no on most questions */
int dry_run; /* don't change any persistent data */
int no_homedir_creation;
int use_keyboxd; /* Use the external keyboxd as storage backend. */
const char *config_filename; /* Name of the used config file. */
char *agent_program;
char *keyboxd_program;
session_env_t session_env;
char *lc_ctype;
char *lc_messages;
int autostart;
char *dirmngr_program;
int disable_dirmngr; /* Do not do any dirmngr calls. */
const char *protect_tool_program;
char *outfile; /* name of output file */
int with_key_data;/* include raw key in the column delimited output */
int fingerprint; /* list fingerprints in all key listings */
int with_md5_fingerprint; /* Also print an MD5 fingerprint for
standard key listings. */
int with_keygrip; /* Option --with-keygrip active. */
int with_key_screening; /* Option --with-key-screening active. */
int no_pretty_dn; /* Option --no-pretty-dn */
int pinentry_mode;
int request_origin;
int armor; /* force base64 armoring (see also ctrl.with_base64) */
int no_armor; /* don't try to figure out whether data is base64 armored*/
const char *p12_charset; /* Use this charset for encoding the
pkcs#12 passphrase. */
const char *def_cipher_algoid; /* cipher algorithm to use if
nothing else is specified */
int def_compress_algo; /* Ditto for compress algorithm */
int forced_digest_algo; /* User forced hash algorithm. */
int force_ecdh_sha1kdf; /* Only for debugging and testing. */
char *def_recipient; /* userID of the default recipient */
int def_recipient_self; /* The default recipient is the default key */
int no_encrypt_to; /* Ignore all as encrypt to marked recipients. */
char *local_user; /* NULL or argument to -u */
int extra_digest_algo; /* A digest algorithm also used for
verification of signatures. */
int skip_verify; /* do not check signatures on data */
int lock_once; /* Keep lock once they are set */
int ignore_time_conflict; /* Ignore certain time conflicts */
int no_crl_check; /* Don't do a CRL check */
int no_trusted_cert_crl_check; /* Don't run a CRL check for trusted certs. */
int force_crl_refresh; /* Force refreshing the CRL. */
int enable_issuer_based_crl_check; /* Backward compatibility hack. */
int enable_ocsp; /* Default to use OCSP checks. */
char *policy_file; /* full pathname of policy file */
int no_policy_check; /* ignore certificate policies */
int no_chain_validation; /* Bypass all cert chain validity tests */
int ignore_expiration; /* Ignore the notAfter validity checks. */
int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */
int qualsig_approval; /* Set to true if this software has
officially been approved to create an
verify qualified signatures. This is a
runtime option in case we want to check
the integrity of the software at
runtime. */
int no_qes_note; /* Do not print a note that the software
* has not been approved for creating or
* verifying qualified signatures. */
unsigned int min_rsa_length; /* Used for compliance checks. */
strlist_t keyserver;
/* A list of certificate extension OIDs which are ignored so that
one can claim that a critical extension has been handled. One
OID per string. */
strlist_t ignored_cert_extensions;
/* A list of OIDs which will be used to ignore certificates with
* sunch an OID during --learn-card. */
strlist_t ignore_cert_with_oid;
/* The current compliance mode. */
enum gnupg_compliance_mode compliance;
/* Fail if an operation can't be done in the requested compliance
* mode. */
int require_compliance;
/* Enable always-trust mode - note that there is also server option
* for this. */
int always_trust;
/* Enable creation of authenticode signatures. */
int authenticode;
/* A list of extra attributes put into a signed data object. For a
* signed each attribute each string has the format:
* <oid>:s:<hex_or_filename>
* and for an unsigned attribute
* <oid>:u:<hex_or_filename>
* The OID is in the usual dotted decimal for. The HEX_OR_FILENAME
* is either a list of hex digits or a filename with the DER encoded
* value. A filename is detected by the presence of a slash in the
* HEX_OR_FILENAME. The actual value needs to be encoded as a SET OF
* attribute values. */
strlist_t attributes;
/* The list of --assert-signer option values. Note: The values are
* modified to uppercase if they represent a fingerrint */
strlist_t assert_signer_list;
/* Compatibility flags (COMPAT_FLAG_xxxx). */
unsigned int compat_flags;
} opt;
/* Debug values and macros. */
#define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */
#define DBG_MPI_VALUE 2 /* debug mpi details */
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
#define DBG_CACHE_VALUE 64 /* debug the caching */
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
#define DBG_IPC_VALUE 1024 /* debug assuan communication */
#define DBG_CLOCK_VALUE 4096
#define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */
#define DBG_X509 (opt.debug & DBG_X509_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
#define DBG_IPC (opt.debug & DBG_IPC_VALUE)
#define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE)
#define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE)
/* Compatibility flags */
/* Telesec RSA cards produced for NRW in 2022 came with only the
* keyAgreement bit set. This flag allows there use for encryption
* anyway. Example cert:
* Issuer: /CN=DOI CA 10a/OU=DOI/O=PKI-1-Verwaltung/C=DE
* key usage: digitalSignature nonRepudiation keyAgreement
* policies: 1.3.6.1.4.1.7924.1.1:N:
*/
#define COMPAT_ALLOW_KA_TO_ENCR 1
/* Not actually a compatibiliy flag but useful to limit the
* required memory for a validated key listing. */
#define COMPAT_NO_CHAIN_CACHE 2
/* Ditto. But here to disable the keyinfo and istrusted cache. */
#define COMPAT_NO_KEYINFO_CACHE 4
/* Forward declaration for an object defined in server.c */
struct server_local_s;
/* Object used to keep state locally in keydb.c */
struct keydb_local_s;
typedef struct keydb_local_s *keydb_local_t;
/* On object used to keep a track of already known certificates. */
struct cert_cache_item_s
{
struct cert_cache_item_s *next;
unsigned char fpr[20]; /* The certificate's fingerprint. */
ksba_cert_t result; /* The resulting certificate (ie. the issuer). */
};
typedef struct cert_cache_item_s *cert_cache_item_t;
/* On object used to keep a KEYINFO data from the agent. */
struct keyinfo_cache_item_s
{
struct keyinfo_cache_item_s *next;
char *serialno; /* Malloced serialnumber of a card. */
char hexgrip[1]; /* The keygrip in hexformat. */
};
typedef struct keyinfo_cache_item_s *keyinfo_cache_item_t;
/* Session control object. This object is passed down to most
functions. Note that the default values for it are set by
gpgsm_init_default_ctrl(). */
struct server_control_s
{
int no_server; /* We are not running under server control */
int status_fd; /* Only for non-server mode */
struct server_local_s *server_local;
keydb_local_t keydb_local; /* Local data for call-keyboxd.c */
audit_ctx_t audit; /* NULL or a context for the audit subsystem. */
int agent_seen; /* Flag indicating that the gpg-agent has been
accessed. */
int with_colons; /* Use column delimited output format */
int with_secret; /* Mark secret keys in a public key listing. */
int with_chain; /* Include the certifying certs in a listing */
int with_validation;/* Validate each key while listing. */
int with_ephemeral_keys; /* Include ephemeral flagged keys in the
keylisting. */
int autodetect_encoding; /* Try to detect the input encoding */
int is_pem; /* Is in PEM format */
int is_base64; /* is in plain base-64 format */
/* If > 0 a hint with the expected number of input data bytes. This
* is not necessary an exact number but intended to be used for
* progress info and to decide on how to allocate buffers. */
uint64_t input_size_hint;
int no_protection; /* No passphrase for PKCS#12 export. */
int create_base64; /* Create base64 encoded output */
int create_pem; /* create PEM output */
const char *pem_name; /* PEM name to use */
int include_certs; /* -1 to send all certificates in the chain
along with a signature or the number of
certificates up the chain (0 = none, 1 = only
signer) */
int use_ocsp; /* Set to true if OCSP should be used. */
int validation_model; /* 0 := standard model (shell),
1 := chain model,
2 := STEED model. */
int offline; /* If true gpgsm won't do any network access. */
int always_trust; /* True in always-trust mode; see also
* opt.always-trust. */
/* The current time. Used as a helper in certchain.c. */
ksba_isotime_t current_time;
/* The revocation info. Used as a helper inc ertchain.c */
gnupg_isotime_t revoked_at;
char *revocation_reason;
/* The cache used to find the parent cert. */
cert_cache_item_t parent_cert_cache;
/* Cache of recently gathered KEYINFO data. */
keyinfo_cache_item_t keyinfo_cache;
int keyinfo_cache_valid;
};
/* An object to keep a list of certificates. */
struct certlist_s
{
struct certlist_s *next;
ksba_cert_t cert;
int is_encrypt_to; /* True if the certificate has been set through
the --encrypto-to option. */
int pk_algo; /* The PK_ALGO from CERT or 0 if not yet known. */
int hash_algo; /* Used to track the hash algorithm to use. */
const char *hash_algo_oid; /* And the corresponding OID. */
};
typedef struct certlist_s *certlist_t;
/* A structure carrying information about trusted root certificates. */
struct rootca_flags_s
{
unsigned int valid:1; /* The rest of the structure has valid
information. */
unsigned int relax:1; /* Relax checking of root certificates. */
unsigned int chain_model:1; /* Root requires the use of the chain model. */
unsigned int qualified:1; /* Root CA used for qualified signatures. */
unsigned int noconsent:1; /* Consent is not required "qualified". */
unsigned int de_vs:1; /* Root CA is de-vs compliant. */
};
/*-- gpgsm.c --*/
extern int gpgsm_errors_seen;
extern int assert_signer_true;
void gpgsm_exit (int rc);
void gpgsm_init_default_ctrl (struct server_control_s *ctrl);
void gpgsm_deinit_default_ctrl (ctrl_t ctrl);
int gpgsm_parse_validation_model (const char *model);
/*-- server.c --*/
void gpgsm_server (certlist_t default_recplist);
void gpgsm_init_statusfp (ctrl_t ctrl);
gpg_error_t gpgsm_status (ctrl_t ctrl, int no, const char *text);
gpg_error_t gpgsm_status2 (ctrl_t ctrl, int no, ...) GPGRT_ATTR_SENTINEL(0);
gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
gpg_err_code_t ec);
gpg_error_t gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text,
gpg_error_t err);
void gpgsm_exit_failure_status (void);
gpg_error_t gpgsm_progress_cb (ctrl_t ctrl, uint64_t current, uint64_t total);
gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl,
const unsigned char *line);
/*-- fingerprint --*/
unsigned char *gpgsm_get_fingerprint (ksba_cert_t cert, int algo,
unsigned char *array, int *r_len);
char *gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo);
char *gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo);
unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert,
unsigned long *r_high);
unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array);
char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert);
int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits,
char **r_curve);
int gpgsm_is_ecc_key (ksba_cert_t cert);
char *gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid);
gcry_mpi_t gpgsm_get_rsa_modulus (ksba_cert_t cert);
char *gpgsm_get_certid (ksba_cert_t cert);
/*-- certdump.c --*/
const void *gpgsm_get_serial (ksba_const_sexp_t sn, size_t *r_length);
void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p);
void gpgsm_print_serial_decimal (estream_t fp, ksba_const_sexp_t sn);
void gpgsm_print_time (estream_t fp, ksba_isotime_t t);
void gpgsm_print_name2 (FILE *fp, const char *string, int translate);
void gpgsm_print_name (FILE *fp, const char *string);
void gpgsm_es_print_name (estream_t fp, const char *string);
void gpgsm_es_print_name2 (estream_t fp, const char *string, int translate);
void gpgsm_cert_log_name (const char *text, ksba_cert_t cert);
void gpgsm_dump_cert (const char *text, ksba_cert_t cert);
void gpgsm_dump_serial (ksba_const_sexp_t p);
void gpgsm_dump_time (ksba_isotime_t t);
void gpgsm_dump_string (const char *string);
char *gpgsm_format_serial (ksba_const_sexp_t p);
char *gpgsm_format_name2 (const char *name, int translate);
char *gpgsm_format_name (const char *name);
char *gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer);
char *gpgsm_fpr_and_name_for_status (ksba_cert_t cert);
char *gpgsm_format_keydesc (ksba_cert_t cert);
/*-- certcheck.c --*/
int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert);
int gpgsm_check_cms_signature (ksba_cert_t cert, gcry_sexp_t sigval,
gcry_md_hd_t md,
int hash_algo, unsigned int pkalgoflags,
int *r_pkalgo);
/* fixme: move create functions to another file */
int gpgsm_create_cms_signature (ctrl_t ctrl,
ksba_cert_t cert, gcry_md_hd_t md, int mdalgo,
unsigned char **r_sigval);
/*-- certchain.c --*/
/* Flags used with gpgsm_validate_chain. */
#define VALIDATE_FLAG_NO_DIRMNGR 1
#define VALIDATE_FLAG_CHAIN_MODEL 2
#define VALIDATE_FLAG_STEED 4
#define VALIDATE_FLAG_BYPASS 8 /* No actual validation. */
gpg_error_t gpgsm_walk_cert_chain (ctrl_t ctrl,
ksba_cert_t start, ksba_cert_t *r_next);
int gpgsm_is_root_cert (ksba_cert_t cert);
int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert,
ksba_isotime_t checktime,
ksba_isotime_t r_exptime,
int listmode, estream_t listfp,
unsigned int flags, unsigned int *retflags);
gpg_error_t check_validity_period_cm (ksba_isotime_t current_time,
ksba_isotime_t check_time,
ksba_cert_t subject_cert,
ksba_isotime_t exptime,
int listmode, estream_t listfp, int depth,
int no_log_expired);
int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert);
/*-- certlist.c --*/
int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent);
int gpgsm_cert_use_encrypt_p (ksba_cert_t cert);
int gpgsm_cert_use_verify_p (ksba_cert_t cert);
int gpgsm_cert_use_decrypt_p (ksba_cert_t cert);
int gpgsm_cert_use_cert_p (ksba_cert_t cert);
int gpgsm_cert_use_ocsp_p (ksba_cert_t cert);
int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert);
int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b);
int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert,
certlist_t *listaddr, int is_encrypt_to);
int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
certlist_t *listaddr, int is_encrypt_to);
void gpgsm_release_certlist (certlist_t list);
#define FIND_CERT_ALLOW_AMBIG 1
#define FIND_CERT_WITH_EPHEM 2
int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid,
ksba_cert_t *r_cert, unsigned int flags);
/*-- keylist.c --*/
gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names,
estream_t fp, unsigned int mode);
gpg_error_t gpgsm_show_certs (ctrl_t ctrl, int nfiles, char **files,
estream_t fp);
/*-- import.c --*/
int gpgsm_import (ctrl_t ctrl, estream_t in_fp, int reimport_mode);
int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
estream_t (*of)(const char *fname, const char *mode));
/*-- export.c --*/
void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream);
void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream,
int rawmode);
/*-- delete.c --*/
int gpgsm_delete (ctrl_t ctrl, strlist_t names);
/*-- verify.c --*/
int gpgsm_verify (ctrl_t ctrl, estream_t in_fp, estream_t data_fp,
estream_t out_fp);
/*-- sign.c --*/
int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert);
int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
estream_t data_fp, int detached, estream_t out_fp);
/*-- encrypt.c --*/
int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist,
estream_t in_fp, estream_t out_fp);
/*-- decrypt.c --*/
gpg_error_t ecdh_derive_kek (unsigned char *key, unsigned int keylen,
int hash_algo, const char *wrap_algo_str,
const void *secret, unsigned int secretlen,
const void *ukm, unsigned int ukmlen);
int gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp);
/*-- certreqgen.c --*/
int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream);
/*-- certreqgen-ui.c --*/
void gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t out_stream);
/*-- qualified.c --*/
gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert,
char *country);
gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert);
gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert);
/*-- call-agent.c --*/
void gpgsm_flush_keyinfo_cache (ctrl_t ctrl);
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 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 gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
- ksba_const_sexp_t ciphertext,
+ int use_kem, ksba_const_sexp_t ciphertext,
char **r_buf, size_t *r_buflen);
gpg_error_t gpgsm_agent_genkey (ctrl_t ctrl, int no_protection,
ksba_const_sexp_t keyparms,
ksba_sexp_t *r_pubkey);
int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
ksba_sexp_t *r_pubkey);
int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno);
int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list);
int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr,
struct rootca_flags_s *rootca_flags);
int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip);
int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert);
int gpgsm_agent_learn (ctrl_t ctrl, const char *serialno);
int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl);
gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
char **r_serialno);
gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg,
int repeat, char **r_passphrase);
gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport,
void **r_kek, size_t *r_keklen);
gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl,
const void *key, size_t keylen);
gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip,
const char *desc,
unsigned char **r_result,
size_t *r_resultlen);
/*-- call-dirmngr.c --*/
gpg_error_t gpgsm_dirmngr_isvalid (ctrl_t ctrl,
ksba_cert_t cert, ksba_cert_t issuer_cert,
int use_ocsp,
gnupg_isotime_t r_revoked_at,
char **r_reason);
int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri,
int cache_only,
void (*cb)(void*, ksba_cert_t), void *cb_value);
int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command,
int argc, char **argv);
/*-- misc.c --*/
void gpgsm_print_further_info (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen,
int mdalgo,
unsigned char **r_newsigval,
size_t *r_newsigvallen);
gcry_sexp_t gpgsm_ksba_cms_get_sig_val (ksba_cms_t cms, int idx);
int gpgsm_get_hash_algo_from_sigval (gcry_sexp_t sigval,
unsigned int *r_pkalgo_flags);
#endif /*GPGSM_H*/

File Metadata

Mime Type
text/x-diff
Expires
Sat, Feb 7, 7:38 AM (1 d, 10 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
4a/96/edb2c5a17c20c5bcf0b76cfd26f4

Event Timeline