diff --git a/common/util.h b/common/util.h index c6d19c64b..f3722812d 100644 --- a/common/util.h +++ b/common/util.h @@ -1,380 +1,385 @@ /* util.h - Utility functions for GnuPG * Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute and/or modify this * part of GnuPG under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * 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 copies of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, see . */ #ifndef GNUPG_COMMON_UTIL_H #define GNUPG_COMMON_UTIL_H #include /* We need this for the memory function protos. */ #include /* We need errno. */ #include /* We need gpg_error_t and estream. */ /* These error codes are used but not defined in the required * libgpg-error version. Define them here. * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) */ #if GPG_ERROR_VERSION_NUMBER < 0x011a00 /* 1.26 */ # define GPG_ERR_UNKNOWN_FLAG 309 # define GPG_ERR_INV_ORDER 310 # define GPG_ERR_ALREADY_FETCHED 311 # define GPG_ERR_TRY_LATER 312 # define GPG_ERR_SYSTEM_BUG 666 # define GPG_ERR_DNS_UNKNOWN 711 # define GPG_ERR_DNS_SECTION 712 # define GPG_ERR_DNS_ADDRESS 713 # define GPG_ERR_DNS_NO_QUERY 714 # define GPG_ERR_DNS_NO_ANSWER 715 # define GPG_ERR_DNS_CLOSED 716 # define GPG_ERR_DNS_VERIFY 717 # define GPG_ERR_DNS_TIMEOUT 718 #endif /* Hash function used with libksba. */ #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) +/* The length of the keygrip. This is a SHA-1 hash of the key + * parameters as generated by gcry_pk_get_keygrip. */ +#define KEYGRIP_LEN 20 + + /* Get all the stuff from jnlib. */ #include "../common/logging.h" #include "../common/argparse.h" #include "../common/stringhelp.h" #include "../common/mischelp.h" #include "../common/strlist.h" #include "../common/dotlock.h" #include "../common/utf8conv.h" #include "../common/dynload.h" #include "../common/fwddecl.h" #include "../common/utilproto.h" #include "gettime.h" /* Redefine asprintf by our estream version which uses our own memory allocator.. */ #define asprintf gpgrt_asprintf #define vasprintf gpgrt_vasprintf /* Due to a bug in mingw32's snprintf related to the 'l' modifier and for increased portability we use our snprintf on all systems. */ #undef snprintf #define snprintf gpgrt_snprintf /* Replacements for macros not available with libgpg-error < 1.20. */ /* We need this type even if we are not using libreadline and or we did not include libreadline in the current file. */ #ifndef GNUPG_LIBREADLINE_H_INCLUDED typedef char **rl_completion_func_t (const char *, int, int); #endif /*!GNUPG_LIBREADLINE_H_INCLUDED*/ /* Handy malloc macros - please use only them. */ #define xtrymalloc(a) gcry_malloc ((a)) #define xtrymalloc_secure(a) gcry_malloc_secure ((a)) #define xtrycalloc(a,b) gcry_calloc ((a),(b)) #define xtrycalloc_secure(a,b) gcry_calloc_secure ((a),(b)) #define xtryrealloc(a,b) gcry_realloc ((a),(b)) #define xtrystrdup(a) gcry_strdup ((a)) #define xfree(a) gcry_free ((a)) #define xfree_fnc gcry_free #define xmalloc(a) gcry_xmalloc ((a)) #define xmalloc_secure(a) gcry_xmalloc_secure ((a)) #define xcalloc(a,b) gcry_xcalloc ((a),(b)) #define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b)) #define xrealloc(a,b) gcry_xrealloc ((a),(b)) #define xstrdup(a) gcry_xstrdup ((a)) /* For compatibility with gpg 1.4 we also define these: */ #define xmalloc_clear(a) gcry_xcalloc (1, (a)) #define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a)) /* The default error source of the application. This is different from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the source file and thus is usable in code shared by applications. Defined by init.c. */ extern gpg_err_source_t default_errsource; /* Convenience function to return a gpg-error code for memory allocation failures. This function makes sure that an error will be returned even if accidentally ERRNO is not set. */ static inline gpg_error_t out_of_core (void) { return gpg_error_from_syserror (); } /*-- yesno.c --*/ int answer_is_yes (const char *s); int answer_is_yes_no_default (const char *s, int def_answer); int answer_is_yes_no_quit (const char *s); int answer_is_okay_cancel (const char *s, int def_answer); /*-- xreadline.c --*/ ssize_t read_line (FILE *fp, char **addr_of_buffer, size_t *length_of_buffer, size_t *max_length); /*-- b64enc.c and b64dec.c --*/ struct b64state { unsigned int flags; int idx; int quad_count; FILE *fp; estream_t stream; char *title; unsigned char radbuf[4]; u32 crc; int stop_seen:1; int invalid_encoding:1; gpg_error_t lasterr; }; gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title); gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp, const char *title); gpg_error_t b64enc_write (struct b64state *state, const void *buffer, size_t nbytes); gpg_error_t b64enc_finish (struct b64state *state); gpg_error_t b64dec_start (struct b64state *state, const char *title); gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length, size_t *r_nbytes); gpg_error_t b64dec_finish (struct b64state *state); /*-- sexputil.c */ char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen); void log_printcanon (const char *text, const unsigned char *sexp, size_t sexplen); void log_printsexp (const char *text, gcry_sexp_t sexp); gpg_error_t make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen); gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure, unsigned char **r_buffer, size_t *r_buflen); gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, unsigned char *grip); int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); unsigned char *make_simple_sexp_from_hexstr (const char *line, size_t *nscanned); int hash_algo_from_sigval (const unsigned char *sigval); unsigned char *make_canon_sexp_from_rsa_pk (const void *m, size_t mlen, const void *e, size_t elen, size_t *r_len); gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, unsigned char const **r_n, size_t *r_nlen, unsigned char const **r_e, size_t *r_elen); int get_pk_algo_from_key (gcry_sexp_t key); int get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen); /*-- convert.c --*/ int hex2bin (const char *string, void *buffer, size_t length); int hexcolon2bin (const char *string, void *buffer, size_t length); char *bin2hex (const void *buffer, size_t length, char *stringbuf); char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); const char *hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen); char *hex2str_alloc (const char *hexstring, size_t *r_count); /*-- percent.c --*/ char *percent_plus_escape (const char *string); char *percent_plus_unescape (const char *string, int nulrepl); char *percent_unescape (const char *string, int nulrepl); size_t percent_plus_unescape_inplace (char *string, int nulrepl); size_t percent_unescape_inplace (char *string, int nulrepl); /*-- openpgp-oid.c --*/ gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); char *openpgp_oid_to_str (gcry_mpi_t a); int openpgp_oid_is_ed25519 (gcry_mpi_t a); int openpgp_oid_is_cv25519 (gcry_mpi_t a); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits); const char *openpgp_oid_to_curve (const char *oid, int canon); const char *openpgp_enum_curves (int *idxp); const char *openpgp_is_curve_supported (const char *name, int *r_algo, unsigned int *r_nbits); /*-- homedir.c --*/ const char *standard_homedir (void); const char *default_homedir (void); void gnupg_set_homedir (const char *newdir); const char *gnupg_homedir (void); int gnupg_default_homedir_p (void); const char *gnupg_daemon_rootdir (void); const char *gnupg_socketdir (void); const char *gnupg_sysconfdir (void); const char *gnupg_bindir (void); const char *gnupg_libexecdir (void); const char *gnupg_libdir (void); const char *gnupg_datadir (void); const char *gnupg_localedir (void); const char *gnupg_cachedir (void); const char *dirmngr_socket_name (void); char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info); /* All module names. We also include gpg and gpgsm for the sake for gpgconf. */ #define GNUPG_MODULE_NAME_AGENT 1 #define GNUPG_MODULE_NAME_PINENTRY 2 #define GNUPG_MODULE_NAME_SCDAEMON 3 #define GNUPG_MODULE_NAME_DIRMNGR 4 #define GNUPG_MODULE_NAME_PROTECT_TOOL 5 #define GNUPG_MODULE_NAME_CHECK_PATTERN 6 #define GNUPG_MODULE_NAME_GPGSM 7 #define GNUPG_MODULE_NAME_GPG 8 #define GNUPG_MODULE_NAME_CONNECT_AGENT 9 #define GNUPG_MODULE_NAME_GPGCONF 10 #define GNUPG_MODULE_NAME_DIRMNGR_LDAP 11 #define GNUPG_MODULE_NAME_GPGV 12 const char *gnupg_module_name (int which); void gnupg_module_name_flush_some (void); void gnupg_set_builddir (const char *newdir); /*-- gpgrlhelp.c --*/ void gnupg_rl_initialize (void); /*-- helpfile.c --*/ char *gnupg_get_help_string (const char *key, int only_current_locale); /*-- localename.c --*/ const char *gnupg_messages_locale_name (void); /*-- miscellaneous.c --*/ /* This function is called at startup to tell libgcrypt to use our own logging subsystem. */ void setup_libgcrypt_logging (void); /* Print an out of core message and die. */ void xoutofcore (void); /* Same as estream_asprintf but die on memory failure. */ char *xasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); /* This is now an alias to estream_asprintf. */ char *xtryasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); /* Replacement for gcry_cipher_algo_name. */ const char *gnupg_cipher_algo_name (int algo); void obsolete_option (const char *configname, unsigned int configlineno, const char *name); const char *print_fname_stdout (const char *s); const char *print_fname_stdin (const char *s); void print_utf8_buffer3 (estream_t fp, const void *p, size_t n, const char *delim); void print_utf8_buffer2 (estream_t fp, const void *p, size_t n, int delim); void print_utf8_buffer (estream_t fp, const void *p, size_t n); void print_utf8_string (estream_t stream, const char *p); void print_hexstring (FILE *fp, const void *buffer, size_t length, int reserved); char *try_make_printable_string (const void *p, size_t n, int delim); char *make_printable_string (const void *p, size_t n, int delim); int is_file_compressed (const char *s, int *ret_rc); int match_multistr (const char *multistr,const char *match); int gnupg_compare_version (const char *a, const char *b); struct debug_flags_s { unsigned int flag; const char *name; }; int parse_debug_flag (const char *string, unsigned int *debugvar, const struct debug_flags_s *flags); /*-- Simple replacement functions. */ /* We use the gnupg_ttyname macro to be safe not to run into conflicts which an extisting but broken ttyname. */ #if !defined(HAVE_TTYNAME) || defined(HAVE_BROKEN_TTYNAME) # define gnupg_ttyname(n) _gnupg_ttyname ((n)) /* Systems without ttyname (W32) will merely return NULL. */ static inline char * _gnupg_ttyname (int fd) { (void)fd; return NULL; } #else /*HAVE_TTYNAME*/ # define gnupg_ttyname(n) ttyname ((n)) #endif /*HAVE_TTYNAME */ #ifdef HAVE_W32CE_SYSTEM #define getpid() GetCurrentProcessId () char *_gnupg_getenv (const char *name); /* See sysutils.c */ #define getenv(a) _gnupg_getenv ((a)) char *_gnupg_setenv (const char *name); /* See sysutils.c */ #define setenv(a,b,c) _gnupg_setenv ((a),(b),(c)) int _gnupg_isatty (int fd); #define gnupg_isatty(a) _gnupg_isatty ((a)) #else #define gnupg_isatty(a) isatty ((a)) #endif /*-- Macros to replace ctype ones to avoid locale problems. --*/ #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define alphap(p) ((*(p) >= 'A' && *(p) <= 'Z') \ || (*(p) >= 'a' && *(p) <= 'z')) #define alnump(p) (alphap (p) || digitp (p)) #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) /* Note this isn't identical to a C locale isspace() without \f and \v, but works for the purposes used here. */ #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') /* The atoi macros assume that the buffer has only valid digits. */ #define atoi_1(p) (*(p) - '0' ) #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2)) #endif /*GNUPG_COMMON_UTIL_H*/ diff --git a/g10/call-agent.c b/g10/call-agent.c index 3874ac181..684771b75 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1,2343 +1,2343 @@ /* call-agent.c - Divert GPG operations to the agent. * Copyright (C) 2001-2003, 2006-2011, 2013 Free Software Foundation, Inc. * Copyright (C) 2013-2015 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #ifdef HAVE_LOCALE_H #include #endif #include "gpg.h" #include #include "../common/util.h" #include "../common/membuf.h" #include "options.h" #include "../common/i18n.h" #include "../common/asshelp.h" #include "../common/sysutils.h" #include "call-agent.h" #include "../common/status.h" #include "../common/shareddefs.h" #include "../common/host2net.h" #define CONTROL_D ('D' - 'A' + 1) static assuan_context_t agent_ctx = NULL; static int did_early_card_test; struct default_inq_parm_s { ctrl_t ctrl; assuan_context_t ctx; struct { u32 *keyid; u32 *mainkeyid; int pubkey_algo; } keyinfo; }; struct cipher_parm_s { struct default_inq_parm_s *dflt; assuan_context_t ctx; unsigned char *ciphertext; size_t ciphertextlen; }; struct writecert_parm_s { struct default_inq_parm_s *dflt; const unsigned char *certdata; size_t certdatalen; }; struct writekey_parm_s { struct default_inq_parm_s *dflt; const unsigned char *keydata; size_t keydatalen; }; struct genkey_parm_s { struct default_inq_parm_s *dflt; const char *keyparms; const char *passphrase; }; struct import_key_parm_s { struct default_inq_parm_s *dflt; const void *key; size_t keylen; }; struct cache_nonce_parm_s { char **cache_nonce_addr; char **passwd_nonce_addr; }; static gpg_error_t learn_status_cb (void *opaque, const char *line); /* If RC is not 0, write an appropriate status message. */ static void status_sc_op_failure (int rc) { switch (gpg_err_code (rc)) { case 0: break; case GPG_ERR_CANCELED: case GPG_ERR_FULLY_CANCELED: write_status_text (STATUS_SC_OP_FAILURE, "1"); break; case GPG_ERR_BAD_PIN: write_status_text (STATUS_SC_OP_FAILURE, "2"); break; default: write_status (STATUS_SC_OP_FAILURE); break; } } /* 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; if (has_leading_keyword (line, "PINENTRY_LAUNCHED")) { err = gpg_proxy_pinentry_notify (parm->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) { if (have_static_passphrase ()) { const char *s = get_static_passphrase (); err = assuan_send_data (parm->ctx, s, strlen (s)); } else { char *pw; char buf[32]; if (parm->keyinfo.keyid) emit_status_need_passphrase (parm->ctrl, parm->keyinfo.keyid, parm->keyinfo.mainkeyid, parm->keyinfo.pubkey_algo); snprintf (buf, sizeof (buf), "%u", 100); write_status_text (STATUS_INQUIRE_MAXLEN, buf); pw = cpr_get_hidden ("passphrase.enter", _("Enter passphrase: ")); cpr_kill_prompt (); if (*pw == CONTROL_D && !pw[1]) err = gpg_error (GPG_ERR_CANCELED); else err = assuan_send_data (parm->ctx, pw, strlen (pw)); xfree (pw); } } else log_debug ("ignoring gpg-agent inquiry '%s'\n", line); return err; } /* 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 (assuan_context_t ctx, const char *servername, int mode) { gpg_error_t err; char *serverversion; const char *myversion = strusage (13); err = get_assuan_server_version (ctx, mode, &serverversion); if (err) log_log (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED? GPGRT_LOG_INFO : GPGRT_LOG_ERROR, _("error getting version from '%s': %s\n"), servername, gpg_strerror (err)); else if (compare_version_strings (serverversion, myversion) < 0) { char *warn; warn = xtryasprintf (_("server '%s' is older than us (%s < %s)"), servername, serverversion, myversion); if (!warn) err = gpg_error_from_syserror (); else { log_info (_("WARNING: %s\n"), warn); if (!opt.quiet) { log_info (_("Note: Outdated servers may lack important" " security fixes.\n")); log_info (_("Note: Use the command \"%s\" to restart them.\n"), "gpgconf --kill all"); } write_status_strings (STATUS_WARNING, "server_version_mismatch 0", " ", warn, NULL); xfree (warn); } } xfree (serverversion); return err; } #define FLAG_FOR_CARD_SUPPRESS_ERRORS 2 /* 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 flag_for_card) { int rc; (void)ctrl; /* Not yet used. */ /* Fixme: We need a context for each thread or serialize the access to the agent. */ if (agent_ctx) rc = 0; else { rc = start_new_gpg_agent (&agent_ctx, GPG_ERR_SOURCE_DEFAULT, opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, opt.autostart, opt.verbose, DBG_IPC, NULL, NULL); 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 (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); /* Tell the agent about what version we are aware. This is here used to indirectly enable GPG_ERR_FULLY_CANCELED. */ assuan_transact (agent_ctx, "OPTION agent-awareness=2.1.0", 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)); write_status_error ("set_pinentry_mode", 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)); write_status_error ("random-compliance", rc); } } #endif /*HAVE_W32_SYSTEM*/ } } if (!rc && flag_for_card && !did_early_card_test) { /* Request the serial number of the card for an early test. */ struct agent_card_info_s info; memset (&info, 0, sizeof info); if (!(flag_for_card & FLAG_FOR_CARD_SUPPRESS_ERRORS)) rc = warn_version_mismatch (agent_ctx, SCDAEMON_NAME, 2); if (!rc) rc = assuan_transact (agent_ctx, "SCD SERIALNO openpgp", NULL, NULL, NULL, NULL, learn_status_cb, &info); if (rc && !(flag_for_card & FLAG_FOR_CARD_SUPPRESS_ERRORS)) { switch (gpg_err_code (rc)) { case GPG_ERR_NOT_SUPPORTED: case GPG_ERR_NO_SCDAEMON: write_status_text (STATUS_CARDCTRL, "6"); break; case GPG_ERR_OBJ_TERM_STATE: write_status_text (STATUS_CARDCTRL, "7"); break; default: write_status_text (STATUS_CARDCTRL, "4"); log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc)); break; } } if (!rc && is_status_enabled () && info.serialno) { char *buf; buf = xasprintf ("3 %s", info.serialno); write_status_text (STATUS_CARDCTRL, buf); xfree (buf); } agent_release_card_info (&info); if (!rc) did_early_card_test = 1; } return rc; } /* Return a new malloced string by unescaping the string S. Escaping is percent escaping and '+'/space mapping. A binary nul will silently be replaced by a 0xFF. Function returns NULL to indicate an out of memory status. */ static char * unescape_status_string (const unsigned char *s) { return percent_plus_unescape (s, 0xff); } /* Take a 20 byte hexencoded string and put it into the provided 20 byte buffer FPR in binary format. */ static int unhexify_fpr (const char *hexstr, unsigned char *fpr) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if (*s || (n != 40)) return 0; /* no fingerprint (invalid or wrong length). */ for (s=hexstr, n=0; *s; s += 2, n++) fpr[n] = xtoi_2 (s); return 1; /* okay */ } /* 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; } /* This is a dummy data line callback. */ static gpg_error_t dummy_data_cb (void *opaque, const void *buffer, size_t length) { (void)opaque; (void)buffer; (void)length; return 0; } /* A simple callback used to return the serialnumber of a card. */ static gpg_error_t get_serialno_cb (void *opaque, const char *line) { char **serialno = opaque; const char *keyword = line; const char *s; int keywordlen, n; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { if (*serialno) return gpg_error (GPG_ERR_CONFLICT); /* Unexpected status line. */ for (n=0,s=line; hexdigitp (s); s++, n++) ; if (!n || (n&1)|| !(spacep (s) || !*s) ) return gpg_error (GPG_ERR_ASS_PARAMETER); *serialno = xtrymalloc (n+1); if (!*serialno) return out_of_core (); memcpy (*serialno, line, n); (*serialno)[n] = 0; } return 0; } /* Release the card info structure INFO. */ void agent_release_card_info (struct agent_card_info_s *info) { int i; if (!info) return; xfree (info->reader); info->reader = NULL; xfree (info->serialno); info->serialno = NULL; xfree (info->apptype); info->apptype = NULL; xfree (info->disp_name); info->disp_name = NULL; xfree (info->disp_lang); info->disp_lang = NULL; xfree (info->pubkey_url); info->pubkey_url = NULL; xfree (info->login_data); info->login_data = NULL; info->cafpr1valid = info->cafpr2valid = info->cafpr3valid = 0; info->fpr1valid = info->fpr2valid = info->fpr3valid = 0; for (i=0; i < DIM(info->private_do); i++) { xfree (info->private_do[i]); info->private_do[i] = NULL; } } static gpg_error_t learn_status_cb (void *opaque, const char *line) { struct agent_card_info_s *parm = opaque; const char *keyword = line; int keywordlen; int i; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 6 && !memcmp (keyword, "READER", keywordlen)) { xfree (parm->reader); parm->reader = unescape_status_string (line); } else if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { xfree (parm->serialno); parm->serialno = store_serialno (line); parm->is_v2 = (strlen (parm->serialno) >= 16 && xtoi_2 (parm->serialno+12) >= 2 ); } else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen)) { xfree (parm->apptype); parm->apptype = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen)) { xfree (parm->disp_name); parm->disp_name = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen)) { xfree (parm->disp_lang); parm->disp_lang = unescape_status_string (line); } else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen)) { parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0; } else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen)) { xfree (parm->pubkey_url); parm->pubkey_url = unescape_status_string (line); } else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen)) { xfree (parm->login_data); parm->login_data = unescape_status_string (line); } else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen)) { parm->sig_counter = strtoul (line, NULL, 0); } else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen)) { char *p, *buf; buf = p = unescape_status_string (line); if (buf) { while (spacep (p)) p++; parm->chv1_cached = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; for (i=0; *p && i < 3; i++) { parm->chvmaxlen[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } for (i=0; *p && i < 3; i++) { parm->chvretry[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } xfree (buf); } } else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen)) { char *p, *p2, *buf; int abool; buf = p = unescape_status_string (line); if (buf) { for (p = strtok (buf, " "); p; p = strtok (NULL, " ")) { p2 = strchr (p, '='); if (p2) { *p2++ = 0; abool = (*p2 == '1'); if (!strcmp (p, "ki")) parm->extcap.ki = abool; else if (!strcmp (p, "aac")) parm->extcap.aac = abool; else if (!strcmp (p, "si")) parm->status_indicator = strtoul (p2, NULL, 10); } } xfree (buf); } } else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1valid = unhexify_fpr (line, parm->fpr1); else if (no == 2) parm->fpr2valid = unhexify_fpr (line, parm->fpr2); else if (no == 3) parm->fpr3valid = unhexify_fpr (line, parm->fpr3); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-TIME", keywordlen)) { int no = atoi (line); while (* line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1time = strtoul (line, NULL, 10); else if (no == 2) parm->fpr2time = strtoul (line, NULL, 10); else if (no == 3) parm->fpr3time = strtoul (line, NULL, 10); } else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1); else if (no == 2) parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2); else if (no == 3) parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-ATTR", keywordlen)) { int keyno = 0; int algo = PUBKEY_ALGO_RSA; int n = 0; sscanf (line, "%d %d %n", &keyno, &algo, &n); keyno--; if (keyno < 0 || keyno >= DIM (parm->key_attr)) return 0; parm->key_attr[keyno].algo = algo; if (algo == PUBKEY_ALGO_RSA) parm->key_attr[keyno].nbits = strtoul (line+n+3, NULL, 10); else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA) parm->key_attr[keyno].curve = openpgp_is_curve_supported (line + n, NULL, NULL); } else if (keywordlen == 12 && !memcmp (keyword, "PRIVATE-DO-", 11) && strchr("1234", keyword[11])) { int no = keyword[11] - '1'; log_assert (no >= 0 && no <= 3); xfree (parm->private_do[no]); parm->private_do[no] = unescape_status_string (line); } return 0; } /* Call the scdaemon to learn about a smartcard */ int agent_scd_learn (struct agent_card_info_s *info, int force) { int rc; struct default_inq_parm_s parm; struct agent_card_info_s dummyinfo; if (!info) info = &dummyinfo; memset (info, 0, sizeof *info); memset (&parm, 0, sizeof parm); rc = start_agent (NULL, 1); if (rc) return rc; parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, force ? "LEARN --sendinfo --force" : "LEARN --sendinfo", dummy_data_cb, NULL, default_inq_cb, &parm, learn_status_cb, info); /* Also try to get the key attributes. */ if (!rc) agent_scd_getattr ("KEY-ATTR", info); if (info == &dummyinfo) agent_release_card_info (info); return rc; } /* Send an APDU to the current card. On success the status word is stored at R_SW. With HEXAPDU being NULL only a RESET command is send to scd. With HEXAPDU being the string "undefined" the command "SERIALNO undefined" is send to scd. */ gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw) { gpg_error_t err; /* Start the agent but not with the card flag so that we do not autoselect the openpgp application. */ err = start_agent (NULL, 0); if (err) return err; if (!hexapdu) { err = assuan_transact (agent_ctx, "SCD RESET", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "undefined")) { err = assuan_transact (agent_ctx, "SCD SERIALNO undefined", NULL, NULL, NULL, NULL, NULL, NULL); } else { char line[ASSUAN_LINELENGTH]; membuf_t mb; unsigned char *data; size_t datalen; init_membuf (&mb, 256); snprintf (line, DIM(line), "SCD APDU %s", hexapdu); err = assuan_transact (agent_ctx, line, put_membuf_cb, &mb, NULL, NULL, NULL, NULL); if (!err) { data = get_membuf (&mb, &datalen); if (!data) err = gpg_error_from_syserror (); else if (datalen < 2) /* Ooops */ err = gpg_error (GPG_ERR_CARD); else { *r_sw = buf16_to_uint (data+datalen-2); } xfree (data); } } return err; } int agent_keytocard (const char *hexgrip, int keyno, int force, const char *serialno, const char *timestamp) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); snprintf (line, DIM(line), "KEYTOCARD %s%s %s OPENPGP.%d %s", force?"--force ": "", hexgrip, serialno, keyno, timestamp); rc = start_agent (NULL, 1); if (rc) return rc; parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); if (rc) return rc; return rc; } /* Call the agent to retrieve a data object. This function returns the data in the same structure as used by the learn command. It is allowed to update such a structure using this command. */ int agent_scd_getattr (const char *name, struct agent_card_info_s *info) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); if (!*name) return gpg_error (GPG_ERR_INV_VALUE); /* We assume that NAME does not need escaping. */ if (12 + strlen (name) > DIM(line)-1) return gpg_error (GPG_ERR_TOO_LARGE); stpcpy (stpcpy (line, "SCD GETATTR "), name); rc = start_agent (NULL, 1); if (rc) return rc; parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, learn_status_cb, info); return rc; } /* Send an setattr command to the SCdaemon. SERIALNO is not actually used here but required by gpg 1.4's implementation of this code in cardglue.c. */ int agent_scd_setattr (const char *name, const unsigned char *value, size_t valuelen, const char *serialno) { int rc; char line[ASSUAN_LINELENGTH]; char *p; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); (void)serialno; if (!*name || !valuelen) return gpg_error (GPG_ERR_INV_VALUE); /* We assume that NAME does not need escaping. */ if (12 + strlen (name) > DIM(line)-1) return gpg_error (GPG_ERR_TOO_LARGE); p = stpcpy (stpcpy (line, "SCD SETATTR "), name); *p++ = ' '; for (; valuelen; value++, valuelen--) { if (p >= line + DIM(line)-5 ) return gpg_error (GPG_ERR_TOO_LARGE); if (*value < ' ' || *value == '+' || *value == '%') { sprintf (p, "%%%02X", *value); p += 3; } else if (*value == ' ') *p++ = '+'; else *p++ = *value; } *p = 0; rc = start_agent (NULL, 1); if (!rc) { parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); } status_sc_op_failure (rc); return rc; } /* Handle a CERTDATA inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the END command. */ static gpg_error_t inq_writecert_parms (void *opaque, const char *line) { int rc; struct writecert_parm_s *parm = opaque; if (has_leading_keyword (line, "CERTDATA")) { rc = assuan_send_data (parm->dflt->ctx, parm->certdata, parm->certdatalen); } else rc = default_inq_cb (parm->dflt, line); return rc; } /* Send a WRITECERT command to the SCdaemon. */ int agent_scd_writecert (const char *certidstr, const unsigned char *certdata, size_t certdatalen) { int rc; char line[ASSUAN_LINELENGTH]; struct writecert_parm_s parms; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 1); if (rc) return rc; memset (&parms, 0, sizeof parms); snprintf (line, DIM(line), "SCD WRITECERT %s", certidstr); dfltparm.ctx = agent_ctx; parms.dflt = &dfltparm; parms.certdata = certdata; parms.certdatalen = certdatalen; rc = assuan_transact (agent_ctx, line, NULL, NULL, inq_writecert_parms, &parms, NULL, NULL); return rc; } /* Handle a KEYDATA inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ static gpg_error_t inq_writekey_parms (void *opaque, const char *line) { int rc; struct writekey_parm_s *parm = opaque; if (has_leading_keyword (line, "KEYDATA")) { rc = assuan_send_data (parm->dflt->ctx, parm->keydata, parm->keydatalen); } else rc = default_inq_cb (parm->dflt, line); return rc; } /* Send a WRITEKEY command to the SCdaemon. */ int agent_scd_writekey (int keyno, const char *serialno, const unsigned char *keydata, size_t keydatalen) { int rc; char line[ASSUAN_LINELENGTH]; struct writekey_parm_s parms; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); (void)serialno; rc = start_agent (NULL, 1); if (rc) return rc; memset (&parms, 0, sizeof parms); snprintf (line, DIM(line), "SCD WRITEKEY --force OPENPGP.%d", keyno); dfltparm.ctx = agent_ctx; parms.dflt = &dfltparm; parms.keydata = keydata; parms.keydatalen = keydatalen; rc = assuan_transact (agent_ctx, line, NULL, NULL, inq_writekey_parms, &parms, NULL, NULL); status_sc_op_failure (rc); return rc; } /* Status callback for the SCD GENKEY command. */ static gpg_error_t scd_genkey_cb (void *opaque, const char *line) { u32 *createtime = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen)) { *createtime = (u32)strtoul (line, NULL, 10); } else if (keywordlen == 8 && !memcmp (keyword, "PROGRESS", keywordlen)) { write_status_text (STATUS_PROGRESS, line); } return 0; } /* Send a GENKEY command to the SCdaemon. If *CREATETIME is not 0, the value will be passed to SCDAEMON with --timestamp option so that the key is created with this. Otherwise, timestamp was generated by SCDEAMON. On success, creation time is stored back to CREATETIME. */ int agent_scd_genkey (int keyno, int force, u32 *createtime) { int rc; char line[ASSUAN_LINELENGTH]; gnupg_isotime_t tbuf; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 1); if (rc) return rc; if (*createtime) epoch2isotime (tbuf, *createtime); else *tbuf = 0; snprintf (line, DIM(line), "SCD GENKEY %s%s %s %d", *tbuf? "--timestamp=":"", tbuf, force? "--force":"", keyno); dfltparm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, scd_genkey_cb, createtime); status_sc_op_failure (rc); return rc; } /* Return the serial number of the card or an appropriate error. The serial number is returned as a hexstring. */ int agent_scd_serialno (char **r_serialno, const char *demand) { int err; char *serialno = NULL; char line[ASSUAN_LINELENGTH]; err = start_agent (NULL, 1 | FLAG_FOR_CARD_SUPPRESS_ERRORS); if (err) return err; if (!demand) strcpy (line, "SCD SERIALNO"); else snprintf (line, DIM(line), "SCD SERIALNO --demand=%s", demand); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, get_serialno_cb, &serialno); if (err) { xfree (serialno); return err; } *r_serialno = serialno; return 0; } /* Send a READCERT command to the SCdaemon. */ int agent_scd_readcert (const char *certidstr, void **r_buf, size_t *r_buflen) { int rc; char line[ASSUAN_LINELENGTH]; membuf_t data; size_t len; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); *r_buf = NULL; rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; init_membuf (&data, 2048); snprintf (line, DIM(line), "SCD READCERT %s", certidstr); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } *r_buf = get_membuf (&data, r_buflen); if (!*r_buf) return gpg_error (GPG_ERR_ENOMEM); return 0; } struct card_cardlist_parm_s { int error; strlist_t list; }; /* Callback function for agent_card_cardlist. */ static gpg_error_t card_cardlist_cb (void *opaque, const char *line) { struct card_cardlist_parm_s *parm = 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)) { const char *s; int n; for (n=0,s=line; hexdigitp (s); s++, n++) ; if (!n || (n&1) || *s) parm->error = gpg_error (GPG_ERR_ASS_PARAMETER); else add_to_strlist (&parm->list, line); } return 0; } /* Return cardlist. */ int agent_scd_cardlist (strlist_t *result) { int err; char line[ASSUAN_LINELENGTH]; struct card_cardlist_parm_s parm; memset (&parm, 0, sizeof parm); *result = NULL; err = start_agent (NULL, 1); if (err) return err; strcpy (line, "SCD GETINFO card_list"); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, card_cardlist_cb, &parm); if (!err && parm.error) err = parm.error; if (!err) *result = parm.list; else free_strlist (parm.list); return 0; } /* Change the PIN of an OpenPGP card or reset the retry counter. CHVNO 1: Change the PIN 2: For v1 cards: Same as 1. For v2 cards: Reset the PIN using the Reset Code. 3: Change the admin PIN 101: Set a new PIN and reset the retry counter 102: For v1 cars: Same as 101. For v2 cards: Set a new Reset Code. SERIALNO is not used. */ int agent_scd_change_pin (int chvno, const char *serialno) { int rc; char line[ASSUAN_LINELENGTH]; const char *reset = ""; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); (void)serialno; if (chvno >= 100) reset = "--reset"; chvno %= 100; rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line), "SCD PASSWD %s %d", reset, chvno); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); status_sc_op_failure (rc); return rc; } /* Perform a CHECKPIN operation. SERIALNO should be the serial number of the card - optionally followed by the fingerprint; however the fingerprint is ignored here. */ int agent_scd_checkpin (const char *serialno) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 1); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line), "SCD CHECKPIN %s", serialno); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); status_sc_op_failure (rc); return rc; } /* Dummy function, only used by the gpg 1.4 implementation. */ void agent_clear_pin_cache (const char *sn) { (void)sn; } /* Note: All strings shall be UTF-8. 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 fpf error code returned. */ gpg_error_t agent_get_passphrase (const char *cache_id, const char *err_msg, const char *prompt, const char *desc_msg, int repeat, int check, char **r_passphrase) { int rc; char line[ASSUAN_LINELENGTH]; char *arg1 = NULL; char *arg2 = NULL; char *arg3 = NULL; char *arg4 = NULL; membuf_t data; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); *r_passphrase = NULL; rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; /* Check that the gpg-agent understands the repeat option. */ if (assuan_transact (agent_ctx, "GETINFO cmd_has_option GET_PASSPHRASE repeat", NULL, NULL, NULL, NULL, NULL, NULL)) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (cache_id && *cache_id) if (!(arg1 = percent_plus_escape (cache_id))) goto no_mem; if (err_msg && *err_msg) if (!(arg2 = percent_plus_escape (err_msg))) goto no_mem; if (prompt && *prompt) if (!(arg3 = percent_plus_escape (prompt))) goto no_mem; if (desc_msg && *desc_msg) if (!(arg4 = percent_plus_escape (desc_msg))) goto no_mem; snprintf (line, DIM(line), "GET_PASSPHRASE --data --repeat=%d%s -- %s %s %s %s", repeat, check? " --check --qualitybar":"", arg1? arg1:"X", arg2? arg2:"X", arg3? arg3:"X", arg4? arg4:"X"); xfree (arg1); xfree (arg2); xfree (arg3); xfree (arg4); init_membuf_secure (&data, 64); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (rc) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); *r_passphrase = get_membuf (&data, NULL); if (!*r_passphrase) rc = gpg_error_from_syserror (); } return rc; no_mem: rc = gpg_error_from_syserror (); xfree (arg1); xfree (arg2); xfree (arg3); xfree (arg4); return rc; } gpg_error_t agent_clear_passphrase (const char *cache_id) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); if (!cache_id || !*cache_id) return 0; rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; snprintf (line, DIM(line), "CLEAR_PASSPHRASE %s", cache_id); return assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); } /* Ask the agent to pop up a confirmation dialog with the text DESC and an okay and cancel button. */ gpg_error_t gpg_agent_get_confirmation (const char *desc) { int rc; char *tmp; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); rc = start_agent (NULL, 0); if (rc) return rc; dfltparm.ctx = agent_ctx; tmp = percent_plus_escape (desc); if (!tmp) return gpg_error_from_syserror (); snprintf (line, DIM(line), "GET_CONFIRMATION %s", tmp); xfree (tmp); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); return rc; } /* Return the S2K iteration count as computed by gpg-agent. */ gpg_error_t agent_get_s2k_count (unsigned long *r_count) { gpg_error_t err; membuf_t data; char *buf; *r_count = 0; err = start_agent (NULL, 0); if (err) return err; init_membuf (&data, 32); err = assuan_transact (agent_ctx, "GETINFO s2k_count", put_membuf_cb, &data, NULL, NULL, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); buf = get_membuf (&data, NULL); if (!buf) err = gpg_error_from_syserror (); else { *r_count = strtoul (buf, NULL, 10); xfree (buf); } } return err; } /* Ask the agent whether a secret key for the given public key is available. Returns 0 if available. */ gpg_error_t agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *hexgrip; err = start_agent (ctrl, 0); if (err) return err; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) return err; snprintf (line, sizeof line, "HAVEKEY %s", hexgrip); xfree (hexgrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); return err; } /* Ask the agent whether a secret key is available for any of the keys (primary or sub) in KEYBLOCK. Returns 0 if available. */ gpg_error_t agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *p; kbnode_t kbctx, node; int nkeys; - unsigned char grip[20]; + unsigned char grip[KEYGRIP_LEN]; err = start_agent (ctrl, 0); if (err) return err; err = gpg_error (GPG_ERR_NO_SECKEY); /* Just in case no key was found in KEYBLOCK. */ p = stpcpy (line, "HAVEKEY"); for (kbctx=NULL, nkeys=0; (node = walk_kbnode (keyblock, &kbctx, 0)); ) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { if (nkeys && ((p - line) + 41) > (ASSUAN_LINELENGTH - 2)) { err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err != gpg_err_code (GPG_ERR_NO_SECKEY)) break; /* Seckey available or unexpected error - ready. */ p = stpcpy (line, "HAVEKEY"); nkeys = 0; } err = keygrip_from_pk (node->pkt->pkt.public_key, grip); if (err) return err; *p++ = ' '; bin2hex (grip, 20, p); p += 40; nkeys++; } if (!err && nkeys) err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); return err; } struct keyinfo_data_parm_s { char *serialno; int cleartext; }; static gpg_error_t keyinfo_status_cb (void *opaque, const char *line) { struct keyinfo_data_parm_s *data = opaque; int is_smartcard; char *s; if ((s = has_leading_keyword (line, "KEYINFO")) && data) { /* Parse the arguments: * 0 1 2 3 4 5 * */ char *fields[6]; if (split_fields (s, fields, DIM (fields)) == 6) { is_smartcard = (fields[1][0] == 'T'); if (is_smartcard && !data->serialno && strcmp (fields[2], "-")) data->serialno = xtrystrdup (fields[2]); /* 'P' for protected, 'C' for clear */ data->cleartext = (fields[5][0] == 'C'); } } 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. if r_cleartext is not NULL, the referenced int will be set to 1 if the agent's copy of the key is stored in the clear, or 0 otherwise */ gpg_error_t agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno, int *r_cleartext) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct keyinfo_data_parm_s keyinfo; memset (&keyinfo, 0,sizeof keyinfo); *r_serialno = NULL; err = start_agent (ctrl, 0); if (err) return err; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); snprintf (line, DIM(line), "KEYINFO %s", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); if (!err && keyinfo.serialno) { /* Sanity check for bad characters. */ if (strpbrk (keyinfo.serialno, ":\n\r")) err = GPG_ERR_INV_VALUE; } if (err) xfree (keyinfo.serialno); else { *r_serialno = keyinfo.serialno; if (r_cleartext) *r_cleartext = keyinfo.cleartext; } return err; } /* Status callback for agent_import_key, agent_export_key and agent_genkey. */ static gpg_error_t cache_nonce_status_cb (void *opaque, const char *line) { struct cache_nonce_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "CACHE_NONCE"))) { if (parm->cache_nonce_addr) { xfree (*parm->cache_nonce_addr); *parm->cache_nonce_addr = xtrystrdup (s); } } else if ((s = has_leading_keyword (line, "PASSWD_NONCE"))) { if (parm->passwd_nonce_addr) { xfree (*parm->passwd_nonce_addr); *parm->passwd_nonce_addr = xtrystrdup (s); } } else if ((s = has_leading_keyword (line, "PROGRESS"))) { if (opt.enable_progress_filter) write_status_text (STATUS_PROGRESS, s); } 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; gpg_error_t err; if (has_leading_keyword (line, "KEYPARAM")) { err = assuan_send_data (parm->dflt->ctx, parm->keyparms, strlen (parm->keyparms)); } else if (has_leading_keyword (line, "NEWPASSWD") && parm->passphrase) { err = assuan_send_data (parm->dflt->ctx, parm->passphrase, strlen (parm->passphrase)); } else err = default_inq_cb (parm->dflt, line); return err; } /* Call the agent to generate a new key. KEYPARMS is the usual S-expression giving the parameters of the key. gpg-agent passes it gcry_pk_genkey. If NO_PROTECTION is true the agent is advised not to protect the generated key. If NO_PROTECTION is not set and PASSPHRASE is not NULL the agent is requested to protect the key with that passphrase instead of asking for one. */ gpg_error_t agent_genkey (ctrl_t ctrl, char **cache_nonce_addr, char **passwd_nonce_addr, const char *keyparms, int no_protection, const char *passphrase, gcry_sexp_t *r_pubkey) { gpg_error_t err; struct genkey_parm_s gk_parm; struct cache_nonce_parm_s cn_parm; struct default_inq_parm_s dfltparm; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_pubkey = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (passwd_nonce_addr && *passwd_nonce_addr) ; /* A RESET would flush the passwd nonce cache. */ else { err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } init_membuf (&data, 1024); gk_parm.dflt = &dfltparm; gk_parm.keyparms = keyparms; gk_parm.passphrase = passphrase; snprintf (line, sizeof line, "GENKEY%s%s%s%s%s", no_protection? " --no-protection" : passphrase ? " --inq-passwd" : /* */ "", passwd_nonce_addr && *passwd_nonce_addr? " --passwd-nonce=":"", passwd_nonce_addr && *passwd_nonce_addr? *passwd_nonce_addr:"", cache_nonce_addr && *cache_nonce_addr? " ":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:""); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, inq_genkey_parms, &gk_parm, cache_nonce_status_cb, &cn_parm); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) err = gpg_error_from_syserror (); else { err = gcry_sexp_sscan (r_pubkey, NULL, buf, len); xfree (buf); } return err; } /* 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). */ gpg_error_t agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, unsigned char **r_pubkey) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_pubkey = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; err = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; snprintf (line, DIM(line), "READKEY %s%s", fromcard? "--card ":"", hexkeygrip); init_membuf (&data, 1024); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); 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 do a sign operation using the key identified by the hex string KEYGRIP. DESC is a description of the key to be displayed if the agent needs to ask for the PIN. DIGEST and DIGESTLEN is the hash value to sign and DIGESTALGO the algorithm id used to compute the digest. If CACHE_NONCE is used the agent is advised to first try a passphrase associated with that nonce. */ gpg_error_t agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *keygrip, const char *desc, u32 *keyid, u32 *mainkeyid, int pubkey_algo, unsigned char *digest, size_t digestlen, int digestalgo, gcry_sexp_t *r_sigval) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; membuf_t data; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; *r_sigval = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (digestlen*2 + 50 > DIM(line)) return gpg_error (GPG_ERR_GENERAL); err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; snprintf (line, DIM(line), "SIGKEY %s", keygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; 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, sizeof line, "SETHASH %d ", digestalgo); bin2hex (digest, digestlen, line + strlen (line)); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; init_membuf (&data, 1024); snprintf (line, sizeof line, "PKSIGN%s%s", cache_nonce? " -- ":"", cache_nonce? cache_nonce:""); if (DBG_CLOCK) log_clock ("enter signing"); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (DBG_CLOCK) log_clock ("leave signing"); if (err) xfree (get_membuf (&data, NULL)); else { unsigned char *buf; size_t len; buf = get_membuf (&data, &len); if (!buf) err = gpg_error_from_syserror (); else { err = gcry_sexp_sscan (r_sigval, NULL, buf, len); xfree (buf); } } return err; } /* 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->dflt->ctx, parm->ciphertext, parm->ciphertextlen); assuan_end_confidential (parm->ctx); } else rc = default_inq_cb (parm->dflt, line); return rc; } /* Check whether there is any padding info from the agent. */ static gpg_error_t padding_info_cb (void *opaque, const char *line) { int *r_padding = opaque; const char *s; if ((s=has_leading_keyword (line, "PADDING"))) { *r_padding = atoi (s); } return 0; } /* Call the agent to do a decrypt operation using the key identified by the hex string KEYGRIP and the input data S_CIPHERTEXT. On the success the decoded value is stored verbatim at R_BUF and its length at R_BUF; the callers needs to release it. KEYID, MAINKEYID and PUBKEY_ALGO are used to construct additional promots or status messages. The padding information is stored at R_PADDING with -1 for not known. */ gpg_error_t agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, u32 *keyid, u32 *mainkeyid, int pubkey_algo, gcry_sexp_t s_ciphertext, unsigned char **r_buf, size_t *r_buflen, int *r_padding) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; membuf_t data; size_t n, len; char *p, *buf, *endp; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; if (!keygrip || strlen(keygrip) != 40 || !s_ciphertext || !r_buf || !r_buflen || !r_padding) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; *r_padding = -1; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; err = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; snprintf (line, sizeof line, "SETKEY %s", keygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; 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; } init_membuf_secure (&data, 1024); { struct cipher_parm_s parm; parm.dflt = &dfltparm; parm.ctx = agent_ctx; err = make_canon_sexp (s_ciphertext, &parm.ciphertext, &parm.ciphertextlen); if (err) return err; err = assuan_transact (agent_ctx, "PKDECRYPT", put_membuf_cb, &data, inq_ciphertext_cb, &parm, padding_info_cb, r_padding); xfree (parm.ciphertext); } if (err) { xfree (get_membuf (&data, &len)); return err; } put_membuf (&data, "", 1); /* Make sure it is 0 terminated. */ buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); log_assert (len); /* (we forced Nul termination.) */ if (*buf != '(') { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } if (len < 13 || memcmp (buf, "(5:value", 8) ) /* "(5:valueN:D)\0" */ { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } len -= 10; /* Count only the data of the second part. */ p = buf + 8; /* Skip leading parenthesis and the value tag. */ n = strtoul (p, &endp, 10); if (!n || *endp != ':') { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } endp++; if (endp-p+n > len) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */ } memmove (buf, endp, n); *r_buflen = n; *r_buf = buf; return 0; } /* Retrieve a key encryption key from the agent. With FOREXPORT true the key shall be used 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 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 dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; *r_kek = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.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, &dfltparm, 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")) { err = assuan_send_data (parm->dflt->ctx, parm->key, parm->keylen); } else err = default_inq_cb (parm->dflt, line); return err; } /* Call the agent to import a key into the agent. */ gpg_error_t agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr, const void *key, size_t keylen, int unattended, int force, u32 *keyid, u32 *mainkeyid, int pubkey_algo) { gpg_error_t err; struct import_key_parm_s parm; struct cache_nonce_parm_s cn_parm; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; err = start_agent (ctrl, 0); if (err) return err; dfltparm.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; } parm.dflt = &dfltparm; parm.key = key; parm.keylen = keylen; snprintf (line, sizeof line, "IMPORT_KEY%s%s%s%s", unattended? " --unattended":"", force? " --force":"", cache_nonce_addr && *cache_nonce_addr? " ":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:""); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, NULL, NULL, inq_import_key_parms, &parm, cache_nonce_status_cb, &cn_parm); return err; } /* Receive a secret key from the agent. HEXKEYGRIP is the hexified keygrip, DESC a prompt to be displayed with the agent's passphrase question (needs to be plus+percent escaped). if OPENPGP_PROTECTED is not zero, ensure that the key material is returned in RFC 4880-compatible passphrased-protected form. If CACHE_NONCE_ADDR is not NULL the agent is advised to first try a passphrase associated with that nonce. On success the key is stored as a canonical S-expression at R_RESULT and R_RESULTLEN. */ gpg_error_t agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, int openpgp_protected, char **cache_nonce_addr, unsigned char **r_result, size_t *r_resultlen, u32 *keyid, u32 *mainkeyid, int pubkey_algo) { gpg_error_t err; struct cache_nonce_parm_s cn_parm; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.keyinfo.keyid = keyid; dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; *r_result = NULL; err = start_agent (ctrl, 0); if (err) return err; dfltparm.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%s%s %s", openpgp_protected ? "--openpgp ":"", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", hexkeygrip); init_membuf_secure (&data, 1024); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = NULL; err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, cache_nonce_status_cb, &cn_parm); 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; } /* Ask the agent to delete the key identified by HEXKEYGRIP. If DESC is not NULL, display DESC instead of the default description message. If FORCE is true the agent is advised not to ask for confirmation. */ gpg_error_t agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, int force) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; err = start_agent (ctrl, 0); if (err) return err; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); 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), "DELETE_KEY%s %s", force? " --force":"", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, NULL, NULL); return err; } /* Ask the agent to change the passphrase of the key identified by * HEXKEYGRIP. If DESC is not NULL, display DESC instead of the * default description message. If CACHE_NONCE_ADDR is not NULL the * agent is advised to first try a passphrase associated with that * nonce. If PASSWD_NONCE_ADDR is not NULL the agent will try to use * the passphrase associated with that nonce for the new passphrase. * If VERIFY is true the passphrase is only verified. */ gpg_error_t agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc, int verify, char **cache_nonce_addr, char **passwd_nonce_addr) { gpg_error_t err; struct cache_nonce_parm_s cn_parm; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; err = start_agent (ctrl, 0); if (err) return err; dfltparm.ctx = agent_ctx; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); 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; } if (verify) snprintf (line, DIM(line), "PASSWD %s%s --verify %s", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", hexkeygrip); else snprintf (line, DIM(line), "PASSWD %s%s %s%s %s", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", passwd_nonce_addr && *passwd_nonce_addr? "--passwd-nonce=":"", passwd_nonce_addr && *passwd_nonce_addr? *passwd_nonce_addr:"", hexkeygrip); cn_parm.cache_nonce_addr = cache_nonce_addr; cn_parm.passwd_nonce_addr = passwd_nonce_addr; err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, cache_nonce_status_cb, &cn_parm); return err; } /* Return the version reported by gpg-agent. */ gpg_error_t agent_get_version (ctrl_t ctrl, char **r_version) { gpg_error_t err; err = start_agent (ctrl, 0); if (err) return err; err = get_assuan_server_version (agent_ctx, 0, r_version); return err; } diff --git a/g10/keyid.c b/g10/keyid.c index c519bc581..de38580f2 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -1,978 +1,978 @@ /* keyid.c - key ID and fingerprint handling * Copyright (C) 1998, 1999, 2000, 2001, 2003, * 2004, 2006, 2010 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * Copyright (C) 2016 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 . */ #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "main.h" #include "packet.h" #include "options.h" #include "keydb.h" #include "../common/i18n.h" #include "rmd160.h" #include "../common/host2net.h" #define KEYID_STR_SIZE 19 #ifdef HAVE_UNSIGNED_TIME_T # define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1)) #else /* Error or 32 bit time_t and value after 2038-01-19. */ # define IS_INVALID_TIME_T(a) ((a) < 0) #endif /* Return a letter describing the public key algorithms. */ int pubkey_letter( int algo ) { switch (algo) { case PUBKEY_ALGO_RSA: return 'R' ; case PUBKEY_ALGO_RSA_E: return 'r' ; case PUBKEY_ALGO_RSA_S: return 's' ; case PUBKEY_ALGO_ELGAMAL_E: return 'g' ; case PUBKEY_ALGO_ELGAMAL: return 'G' ; case PUBKEY_ALGO_DSA: return 'D' ; case PUBKEY_ALGO_ECDH: return 'e' ; /* ECC DH (encrypt only) */ case PUBKEY_ALGO_ECDSA: return 'E' ; /* ECC DSA (sign only) */ case PUBKEY_ALGO_EDDSA: return 'E' ; /* ECC EdDSA (sign only) */ default: return '?'; } } /* Return a string describing the public key algorithm and the keysize. For elliptic curves the functions prints the name of the curve because the keysize is a property of the curve. The string is copied to the supplied buffer up a length of BUFSIZE-1. Examples for the output are: "rsa3072" - RSA with 3072 bit "elg1024" - Elgamal with 1024 bit "ed25519" - ECC using the curve Ed25519. "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". "E_1.3.6.1.4.1.11591.2.12242973" ECC with a bogus OID. "unknown_N" - Unknown OpenPGP algorithm N. If the option --legacy-list-mode is active, the output use the legacy format: "3072R" - RSA with 3072 bit "1024g" - Elgamal with 1024 bit "256E" - ECDSA using a curve with 256 bit The macro PUBKEY_STRING_SIZE may be used to allocate a buffer with a suitable size.*/ char * pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) { const char *prefix = NULL; if (opt.legacy_list_mode) { snprintf (buffer, bufsize, "%4u%c", nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo)); return buffer; } switch (pk->pubkey_algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: prefix = "rsa"; break; case PUBKEY_ALGO_ELGAMAL_E: prefix = "elg"; break; case PUBKEY_ALGO_DSA: prefix = "dsa"; break; case PUBKEY_ALGO_ELGAMAL: prefix = "xxx"; break; case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: prefix = ""; break; } if (prefix && *prefix) snprintf (buffer, bufsize, "%s%u", prefix, nbits_from_pk (pk)); else if (prefix) { char *curve = openpgp_oid_to_str (pk->pkey[0]); const char *name = openpgp_oid_to_curve (curve, 0); if (name) snprintf (buffer, bufsize, "%s", name); else if (curve) snprintf (buffer, bufsize, "E_%s", curve); else snprintf (buffer, bufsize, "E_error"); xfree (curve); } else snprintf (buffer, bufsize, "unknown_%u", (unsigned int)pk->pubkey_algo); return buffer; } /* Hash a public key. This function is useful for v4 fingerprints and for v3 or v4 key signing. */ void hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) { unsigned int n = 6; unsigned int nn[PUBKEY_MAX_NPKEY]; byte *pp[PUBKEY_MAX_NPKEY]; int i; unsigned int nbits; size_t nbytes; int npkey = pubkey_get_npkey (pk->pubkey_algo); /* FIXME: We can avoid the extra malloc by calling only the first mpi_print here which computes the required length and calling the real mpi_print only at the end. The speed advantage would only be for ECC (opaque MPIs) or if we could implement an mpi_print variant with a callback handler to do the hashing. */ if (npkey==0 && pk->pkey[0] && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE)) { pp[0] = gcry_mpi_get_opaque (pk->pkey[0], &nbits); nn[0] = (nbits+7)/8; n+=nn[0]; } else { for (i=0; i < npkey; i++ ) { if (!pk->pkey[i]) { /* This case may only happen if the parsing of the MPI failed but the key was anyway created. May happen during "gpg KEYFILE". */ pp[i] = NULL; nn[i] = 0; } else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) { const void *p; p = gcry_mpi_get_opaque (pk->pkey[i], &nbits); pp[i] = xmalloc ((nbits+7)/8); if (p) memcpy (pp[i], p, (nbits+7)/8); else pp[i] = NULL; nn[i] = (nbits+7)/8; n += nn[i]; } else { if (gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, pk->pkey[i])) BUG (); pp[i] = xmalloc (nbytes); if (gcry_mpi_print (GCRYMPI_FMT_PGP, pp[i], nbytes, &nbytes, pk->pkey[i])) BUG (); nn[i] = nbytes; n += nn[i]; } } } gcry_md_putc ( md, 0x99 ); /* ctb */ /* What does it mean if n is greater than 0xFFFF ? */ gcry_md_putc ( md, n >> 8 ); /* 2 byte length header */ gcry_md_putc ( md, n ); gcry_md_putc ( md, pk->version ); gcry_md_putc ( md, pk->timestamp >> 24 ); gcry_md_putc ( md, pk->timestamp >> 16 ); gcry_md_putc ( md, pk->timestamp >> 8 ); gcry_md_putc ( md, pk->timestamp ); gcry_md_putc ( md, pk->pubkey_algo ); if(npkey==0 && pk->pkey[0] && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE)) { if (pp[0]) gcry_md_write (md, pp[0], nn[0]); } else { for(i=0; i < npkey; i++ ) { if (pp[i]) gcry_md_write ( md, pp[i], nn[i] ); xfree(pp[i]); } } } static gcry_md_hd_t do_fingerprint_md( PKT_public_key *pk ) { gcry_md_hd_t md; if (gcry_md_open (&md, DIGEST_ALGO_SHA1, 0)) BUG (); hash_public_key(md,pk); gcry_md_final( md ); return md; } /* fixme: Check whether we can replace this function or if not describe why we need it. */ u32 v3_keyid (gcry_mpi_t a, u32 *ki) { byte *buffer, *p; size_t nbytes; if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &nbytes, a )) BUG (); /* fixme: allocate it on the stack */ buffer = xmalloc (nbytes); if (gcry_mpi_print( GCRYMPI_FMT_USG, buffer, nbytes, NULL, a )) BUG (); if (nbytes < 8) /* oops */ ki[0] = ki[1] = 0; else { p = buffer + nbytes - 8; ki[0] = buf32_to_u32 (p); p += 4; ki[1] = buf32_to_u32 (p); } xfree (buffer); return ki[1]; } /* Return PK's keyid. The memory is owned by PK. */ u32 * pk_keyid (PKT_public_key *pk) { keyid_from_pk (pk, NULL); /* Uncomment this for help tracking down bugs related to keyid or main_keyid not being set correctly. */ #if 0 if (! (pk->main_keyid[0] || pk->main_keyid[1])) log_bug ("pk->main_keyid not set!\n"); if (keyid_cmp (pk->keyid, pk->main_keyid) == 0 && ! pk->flags.primary) log_bug ("keyid and main_keyid are the same, but primary flag not set!\n"); if (keyid_cmp (pk->keyid, pk->main_keyid) != 0 && pk->flags.primary) log_bug ("keyid and main_keyid are different, but primary flag set!\n"); #endif return pk->keyid; } /* Return the keyid of the primary key associated with PK. The memory is owned by PK. */ u32 * pk_main_keyid (PKT_public_key *pk) { /* Uncomment this for help tracking down bugs related to keyid or main_keyid not being set correctly. */ #if 0 if (! (pk->main_keyid[0] || pk->main_keyid[1])) log_bug ("pk->main_keyid not set!\n"); #endif return pk->main_keyid; } /* Copy the keyid in SRC to DEST and return DEST. */ u32 * keyid_copy (u32 *dest, const u32 *src) { dest[0] = src[0]; dest[1] = src[1]; return dest; } char * format_keyid (u32 *keyid, int format, char *buffer, int len) { char tmp[KEYID_STR_SIZE]; if (! buffer) { buffer = tmp; len = sizeof (tmp); } if (format == KF_DEFAULT) format = opt.keyid_format; if (format == KF_DEFAULT) format = KF_NONE; switch (format) { case KF_NONE: if (len) *buffer = 0; break; case KF_SHORT: snprintf (buffer, len, "%08lX", (ulong)keyid[1]); break; case KF_LONG: snprintf (buffer, len, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1]); break; case KF_0xSHORT: snprintf (buffer, len, "0x%08lX", (ulong)keyid[1]); break; case KF_0xLONG: snprintf (buffer, len, "0x%08lX%08lX", (ulong)keyid[0],(ulong)keyid[1]); break; default: BUG(); } if (buffer == tmp) return xstrdup (buffer); return buffer; } size_t keystrlen(void) { int format = opt.keyid_format; if (format == KF_DEFAULT) format = KF_NONE; switch(format) { case KF_NONE: return 0; case KF_SHORT: return 8; case KF_LONG: return 16; case KF_0xSHORT: return 10; case KF_0xLONG: return 18; default: BUG(); } } const char * keystr (u32 *keyid) { static char keyid_str[KEYID_STR_SIZE]; int format = opt.keyid_format; if (format == KF_DEFAULT) format = KF_NONE; if (format == KF_NONE) format = KF_LONG; return format_keyid (keyid, format, keyid_str, sizeof (keyid_str)); } /* This function returns the key id of the main and possible the * subkey as one string. It is used by error messages. */ const char * keystr_with_sub (u32 *main_kid, u32 *sub_kid) { static char buffer[KEYID_STR_SIZE+1+KEYID_STR_SIZE]; char *p; int format = opt.keyid_format; if (format == KF_NONE) format = KF_LONG; format_keyid (main_kid, format, buffer, KEYID_STR_SIZE); if (sub_kid) { p = buffer + strlen (buffer); *p++ = '/'; format_keyid (sub_kid, format, p, KEYID_STR_SIZE); } return buffer; } const char * keystr_from_pk(PKT_public_key *pk) { keyid_from_pk(pk,NULL); return keystr(pk->keyid); } const char * keystr_from_pk_with_sub (PKT_public_key *main_pk, PKT_public_key *sub_pk) { keyid_from_pk (main_pk, NULL); if (sub_pk) keyid_from_pk (sub_pk, NULL); return keystr_with_sub (main_pk->keyid, sub_pk? sub_pk->keyid:NULL); } /* Return PK's key id as a string using the default format. PK owns the storage. */ const char * pk_keyid_str (PKT_public_key *pk) { return keystr (pk_keyid (pk)); } const char * keystr_from_desc(KEYDB_SEARCH_DESC *desc) { switch(desc->mode) { case KEYDB_SEARCH_MODE_LONG_KID: case KEYDB_SEARCH_MODE_SHORT_KID: return keystr(desc->u.kid); case KEYDB_SEARCH_MODE_FPR20: { u32 keyid[2]; keyid[0] = buf32_to_u32 (desc->u.fpr+12); keyid[1] = buf32_to_u32 (desc->u.fpr+16); return keystr(keyid); } case KEYDB_SEARCH_MODE_FPR16: return "?v3 fpr?"; default: BUG(); } } /* * Get the keyid from the public key and put it into keyid * if this is not NULL. Return the 32 low bits of the keyid. */ u32 keyid_from_pk (PKT_public_key *pk, u32 *keyid) { u32 lowbits; u32 dummy_keyid[2]; if (!keyid) keyid = dummy_keyid; if( pk->keyid[0] || pk->keyid[1] ) { keyid[0] = pk->keyid[0]; keyid[1] = pk->keyid[1]; lowbits = keyid[1]; } else { const byte *dp; gcry_md_hd_t md; md = do_fingerprint_md(pk); if(md) { dp = gcry_md_read ( md, 0 ); keyid[0] = buf32_to_u32 (dp+12); keyid[1] = buf32_to_u32 (dp+16); lowbits = keyid[1]; gcry_md_close (md); pk->keyid[0] = keyid[0]; pk->keyid[1] = keyid[1]; } else pk->keyid[0]=pk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF; } return lowbits; } /* * Get the keyid from the fingerprint. This function is simple for most * keys, but has to do a keylookup for old stayle keys. */ u32 keyid_from_fingerprint (ctrl_t ctrl, const byte *fprint, size_t fprint_len, u32 *keyid) { u32 dummy_keyid[2]; if( !keyid ) keyid = dummy_keyid; if (fprint_len != 20) { /* This is special as we have to lookup the key first. */ PKT_public_key pk; int rc; memset (&pk, 0, sizeof pk); rc = get_pubkey_byfprint (ctrl, &pk, NULL, fprint, fprint_len); if( rc ) { log_error("Oops: keyid_from_fingerprint: no pubkey\n"); keyid[0] = 0; keyid[1] = 0; } else keyid_from_pk (&pk, keyid); } else { const byte *dp = fprint; keyid[0] = buf32_to_u32 (dp+12); keyid[1] = buf32_to_u32 (dp+16); } return keyid[1]; } u32 keyid_from_sig (PKT_signature *sig, u32 *keyid) { if( keyid ) { keyid[0] = sig->keyid[0]; keyid[1] = sig->keyid[1]; } return sig->keyid[1]; } byte * namehash_from_uid (PKT_user_id *uid) { if (!uid->namehash) { uid->namehash = xmalloc (20); if (uid->attrib_data) rmd160_hash_buffer (uid->namehash, uid->attrib_data, uid->attrib_len); else rmd160_hash_buffer (uid->namehash, uid->name, uid->len); } return uid->namehash; } /* * Return the number of bits used in PK. */ unsigned int nbits_from_pk (PKT_public_key *pk) { return pubkey_nbits (pk->pubkey_algo, pk->pkey); } /* Convert an UTC TIMESTAMP into an UTC yyyy-mm-dd string. Return * that string. The caller should pass a buffer with at least a size * of MK_DATESTR_SIZE. */ char * mk_datestr (char *buffer, size_t bufsize, u32 timestamp) { time_t atime = timestamp; struct tm *tp; if (IS_INVALID_TIME_T (atime)) strcpy (buffer, "????" "-??" "-??"); /* Mark this as invalid. */ else { tp = gmtime (&atime); snprintf (buffer, bufsize, "%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); } return buffer; } /* * return a string with the creation date of the pk * Note: this is alloced in a static buffer. * Format is: yyyy-mm-dd */ const char * datestr_from_pk (PKT_public_key *pk) { static char buffer[MK_DATESTR_SIZE]; return mk_datestr (buffer, sizeof buffer, pk->timestamp); } const char * datestr_from_sig (PKT_signature *sig ) { static char buffer[MK_DATESTR_SIZE]; return mk_datestr (buffer, sizeof buffer, sig->timestamp); } const char * expirestr_from_pk (PKT_public_key *pk) { static char buffer[MK_DATESTR_SIZE]; if (!pk->expiredate) return _("never "); return mk_datestr (buffer, sizeof buffer, pk->expiredate); } const char * expirestr_from_sig (PKT_signature *sig) { static char buffer[MK_DATESTR_SIZE]; if (!sig->expiredate) return _("never "); return mk_datestr (buffer, sizeof buffer, sig->expiredate); } const char * revokestr_from_pk( PKT_public_key *pk ) { static char buffer[MK_DATESTR_SIZE]; if(!pk->revoked.date) return _("never "); return mk_datestr (buffer, sizeof buffer, pk->revoked.date); } const char * usagestr_from_pk (PKT_public_key *pk, int fill) { static char buffer[10]; int i = 0; unsigned int use = pk->pubkey_usage; if ( use & PUBKEY_USAGE_SIG ) buffer[i++] = 'S'; if ( use & PUBKEY_USAGE_CERT ) buffer[i++] = 'C'; if ( use & PUBKEY_USAGE_ENC ) buffer[i++] = 'E'; if ( (use & PUBKEY_USAGE_AUTH) ) buffer[i++] = 'A'; while (fill && i < 4) buffer[i++] = ' '; buffer[i] = 0; return buffer; } const char * colon_strtime (u32 t) { static char buf[20]; if (!t) return ""; snprintf (buf, sizeof buf, "%lu", (ulong)t); return buf; } const char * colon_datestr_from_pk (PKT_public_key *pk) { static char buf[20]; snprintf (buf, sizeof buf, "%lu", (ulong)pk->timestamp); return buf; } const char * colon_datestr_from_sig (PKT_signature *sig) { static char buf[20]; snprintf (buf, sizeof buf, "%lu", (ulong)sig->timestamp); return buf; } const char * colon_expirestr_from_sig (PKT_signature *sig) { static char buf[20]; if (!sig->expiredate) return ""; snprintf (buf, sizeof buf,"%lu", (ulong)sig->expiredate); return buf; } /* * Return a byte array with the fingerprint for the given PK/SK * The length of the array is returned in ret_len. Caller must free * the array or provide an array of length MAX_FINGERPRINT_LEN. */ byte * fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) { const byte *dp; size_t len; gcry_md_hd_t md; md = do_fingerprint_md(pk); dp = gcry_md_read( md, 0 ); len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); log_assert( len <= MAX_FINGERPRINT_LEN ); if (!array) array = xmalloc ( len ); memcpy (array, dp, len ); pk->keyid[0] = buf32_to_u32 (dp+12); pk->keyid[1] = buf32_to_u32 (dp+16); gcry_md_close( md); if (ret_len) *ret_len = len; return array; } /* Return an allocated buffer with the fingerprint of PK formatted as a plain hexstring. If BUFFER is NULL the result is a malloc'd string. If BUFFER is not NULL the result will be copied into this buffer. In the latter case BUFLEN describes the length of the buffer; if this is too short the function terminates the process. Returns a malloc'ed string or BUFFER. A suitable length for BUFFER is (2*MAX_FINGERPRINT_LEN + 1). */ char * hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen) { unsigned char fpr[MAX_FINGERPRINT_LEN]; size_t len; fingerprint_from_pk (pk, fpr, &len); if (!buffer) buffer = xmalloc (2 * len + 1); else if (buflen < 2*len+1) log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen); bin2hex (fpr, len, buffer); return buffer; } /* Pretty print a hex fingerprint. If BUFFER is NULL the result is a malloc'd string. If BUFFER is not NULL the result will be copied into this buffer. In the latter case BUFLEN describes the length of the buffer; if this is too short the function terminates the process. Returns a malloc'ed string or BUFFER. A suitable length for BUFFER is (MAX_FORMATTED_FINGERPRINT_LEN + 1). */ char * format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen) { int hexlen = strlen (fingerprint); int space; int i, j; if (hexlen == 40) /* v4 fingerprint */ { space = (/* The characters and the NUL. */ 40 + 1 /* After every fourth character, we add a space (except the last). */ + 40 / 4 - 1 /* Half way through we add a second space. */ + 1); } else /* Other fingerprint versions - print as is. */ { space = hexlen + 1; } if (!buffer) buffer = xmalloc (space); else if (buflen < space) log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen); if (hexlen == 40) /* v4 fingerprint */ { for (i = 0, j = 0; i < 40; i ++) { if (i && i % 4 == 0) buffer[j ++] = ' '; if (i == 40 / 2) buffer[j ++] = ' '; buffer[j ++] = fingerprint[i]; } buffer[j ++] = 0; log_assert (j == space); } else { strcpy (buffer, fingerprint); } return buffer; } /* Return the so called KEYGRIP which is the SHA-1 hash of the public key parameters expressed as an canoncial encoded S-Exp. ARRAY must be 20 bytes long. Returns 0 on success or an error code. */ gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array) { gpg_error_t err; gcry_sexp_t s_pkey; if (DBG_PACKET) log_debug ("get_keygrip for public key\n"); switch (pk->pubkey_algo) { case GCRY_PK_DSA: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", pk->pkey[0], pk->pkey[1], pk->pkey[2], pk->pkey[3]); break; case GCRY_PK_ELG: case GCRY_PK_ELG_E: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", pk->pkey[0], pk->pkey[1], pk->pkey[2]); break; case GCRY_PK_RSA: case GCRY_PK_RSA_S: case GCRY_PK_RSA_E: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))", pk->pkey[0], pk->pkey[1]); break; case PUBKEY_ALGO_EDDSA: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_ECDH: { char *curve = openpgp_oid_to_str (pk->pkey[0]); if (!curve) err = gpg_error_from_syserror (); else { err = gcry_sexp_build (&s_pkey, NULL, pk->pubkey_algo == PUBKEY_ALGO_EDDSA? "(public-key(ecc(curve%s)(flags eddsa)(q%m)))": (pk->pubkey_algo == PUBKEY_ALGO_ECDH && openpgp_oid_is_cv25519 (pk->pkey[0]))? "(public-key(ecc(curve%s)(flags djb-tweak)(q%m)))": "(public-key(ecc(curve%s)(q%m)))", curve, pk->pkey[1]); xfree (curve); } } break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; } if (err) return err; if (!gcry_pk_get_keygrip (s_pkey, array)) { log_info ("error computing keygrip\n"); memset (array, 0, 20); err = gpg_error (GPG_ERR_GENERAL); } else { if (DBG_PACKET) log_printhex ("keygrip=", array, 20); /* FIXME: Save the keygrip in PK. */ } gcry_sexp_release (s_pkey); return err; } /* Store an allocated buffer with the keygrip of PK encoded as a hexstring at r_GRIP. Returns 0 on success. */ gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) { gpg_error_t err; - unsigned char grip[20]; + unsigned char grip[KEYGRIP_LEN]; *r_grip = NULL; err = keygrip_from_pk (pk, grip); if (!err) { - char * buf = xtrymalloc (20*2+1); + char * buf = xtrymalloc (KEYGRIP_LEN * 2 + 1); if (!buf) err = gpg_error_from_syserror (); else { - bin2hex (grip, 20, buf); + bin2hex (grip, KEYGRIP_LEN, buf); *r_grip = buf; } } return err; }