diff --git a/g10/call-agent.c b/g10/call-agent.c index a49c987fa..31943d7df 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -1,3381 +1,3404 @@ /* 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 * Copyright (C) 2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #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" #include "../common/ttyio.h" #define CONTROL_D ('D' - 'A' + 1) static assuan_context_t agent_ctx = NULL; static int did_early_card_test; struct confirm_parm_s { char *desc; char *ok; char *notok; }; struct default_inq_parm_s { ctrl_t ctrl; assuan_context_t ctx; struct { u32 *keyid; u32 *mainkeyid; int pubkey_algo; } keyinfo; struct confirm_parm_s *confirm; }; 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: case GPG_ERR_BAD_RESET_CODE: 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; const char *s; 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) { assuan_begin_confidential (parm->ctx); if (have_static_passphrase ()) { 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); } assuan_end_confidential (parm->ctx); } else if ((s = has_leading_keyword (line, "CONFIRM")) && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK && parm->confirm) { int ask = atoi (s); int yes; if (ask) { yes = cpr_get_answer_is_yes (NULL, parm->confirm->desc); if (yes) err = assuan_send_data (parm->ctx, NULL, 0); else err = gpg_error (GPG_ERR_NOT_CONFIRMED); } else { tty_printf ("%s", parm->confirm->desc); err = assuan_send_data (parm->ctx, NULL, 0); } } 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) { return warn_server_version_mismatch (ctx, servername, mode, write_status_strings2, NULL, !opt.quiet); } #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?ASSHELP_FLAG_AUTOSTART:0, 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); } } /* 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)); write_status_error ("set_request_origin", 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, opt.flags.use_only_openpgp_card? "SCD SERIALNO openpgp" : "SCD SERIALNO", 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 card 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 or 32 byte hexencoded string and put it into the provided * FPRLEN byte long buffer FPR in binary format. Returns the actual * used length of the FPR buffer or 0 on error. */ static unsigned int unhexify_fpr (const char *hexstr, unsigned char *fpr, unsigned int fprlen) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if ((*s && *s != ' ') || !(n == 40 || n == 64)) return 0; /* no fingerprint (invalid or wrong length). */ for (s=hexstr, n=0; *s && n < fprlen; s += 2, n++) fpr[n] = xtoi_2 (s); return (n == 20 || n == 32)? n : 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; } /* 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->manufacturer_name); info->manufacturer_name = 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->cafpr1len = info->cafpr2len = info->cafpr3len = 0; info->fpr1len = info->fpr2len = info->fpr3len = 0; for (i=0; i < DIM(info->private_do); i++) { xfree (info->private_do[i]); info->private_do[i] = NULL; } for (i=0; i < DIM(info->supported_keyalgo); i++) { free_strlist (info->supported_keyalgo[i]); info->supported_keyalgo[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; char *endp; 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) == 0 /* Yubikey */ || 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 == 10 && !memcmp (keyword, "APPVERSION", keywordlen)) { unsigned int val = 0; sscanf (line, "%x", &val); parm->appversion = val; } 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, "bt")) parm->extcap.bt = abool; else if (!strcmp (p, "kdf")) parm->extcap.kdf = 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->fpr1len = unhexify_fpr (line, parm->fpr1, sizeof parm->fpr1); else if (no == 2) parm->fpr2len = unhexify_fpr (line, parm->fpr2, sizeof parm->fpr2); else if (no == 3) parm->fpr3len = unhexify_fpr (line, parm->fpr3, sizeof 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 == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { const char *hexgrp = line; int no; while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (strncmp (line, "OPENPGP.", 8)) ; else if ((no = atoi (line+8)) == 1) unhexify_fpr (hexgrp, parm->grp1, sizeof parm->grp1); else if (no == 2) unhexify_fpr (hexgrp, parm->grp2, sizeof parm->grp2); else if (no == 3) unhexify_fpr (hexgrp, parm->grp3, sizeof parm->grp3); } 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->cafpr1len = unhexify_fpr (line, parm->cafpr1,sizeof parm->cafpr1); else if (no == 2) parm->cafpr2len = unhexify_fpr (line, parm->cafpr2,sizeof parm->cafpr2); else if (no == 3) parm->cafpr3len = unhexify_fpr (line, parm->cafpr3,sizeof 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); } else if (keywordlen == 12 && !memcmp (keyword, "MANUFACTURER", 12)) { xfree (parm->manufacturer_name); parm->manufacturer_name = NULL; parm->manufacturer_id = strtoul (line, &endp, 0); while (endp && spacep (endp)) endp++; if (endp && *endp) parm->manufacturer_name = xstrdup (endp); } else if (keywordlen == 3 && !memcmp (keyword, "KDF", 3)) { unsigned char *data = unescape_status_string (line); if (data[2] != 0x03) parm->kdf_do_enabled = 0; else if (data[22] != 0x85) parm->kdf_do_enabled = 1; else parm->kdf_do_enabled = 2; xfree (data); } else if (keywordlen == 5 && !memcmp (keyword, "UIF-", 4) && strchr("123", keyword[4])) { unsigned char *data; int no = keyword[4] - '1'; log_assert (no >= 0 && no <= 2); data = unescape_status_string (line); parm->uif[no] = (data[0] != 0xff); xfree (data); } else if (keywordlen == 13 && !memcmp (keyword, "KEY-ATTR-INFO", 13)) { if (!strncmp (line, "OPENPGP.", 8)) { int no; line += 8; no = atoi (line); if (no >= 1 && no <= 3) { no--; line++; while (spacep (line)) line++; append_to_strlist (&parm->supported_keyalgo[no], xstrdup (line)); } } /* Skip when it's not "OPENPGP.[123]". */ } return 0; } /* Call the scdaemon to learn about a smartcard. Note that in * contradiction to the function's name, gpg-agent's LEARN command is * used and not the low-level "SCD LEARN". * Used by: * card-util.c * keyedit_menu * card_store_key_with_backup (Woth force to remove secret key data) */ 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; } struct keypairinfo_cb_parm_s { keypair_info_t kpinfo; keypair_info_t *kpinfo_tail; }; /* Callback for the agent_scd_keypairinfo function. */ static gpg_error_t scd_keypairinfo_status_cb (void *opaque, const char *line) { struct keypairinfo_cb_parm_s *parm = opaque; gpg_error_t err = 0; const char *keyword = line; int keywordlen; char *line_buffer = NULL; keypair_info_t kpi = NULL; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { /* The format of such a line is: * KEYPAIRINFO [usage] [keytime] [algostr] */ const char *fields[4]; int nfields; const char *hexgrp, *keyref, *usage; time_t atime; u32 keytime; line_buffer = xtrystrdup (line); if (!line_buffer) { err = gpg_error_from_syserror (); goto leave; } if ((nfields = split_fields (line_buffer, fields, DIM (fields))) < 2) goto leave; /* not enough args - invalid status line - ignore */ hexgrp = fields[0]; keyref = fields[1]; if (nfields > 2) usage = fields[2]; else usage = ""; if (nfields > 3) { atime = parse_timestamp (fields[3], NULL); if (atime == (time_t)(-1)) atime = 0; keytime = atime; } else keytime = 0; kpi = xtrycalloc (1, sizeof *kpi); if (!kpi) { err = gpg_error_from_syserror (); goto leave; } if (*hexgrp == 'X' && !hexgrp[1]) *kpi->keygrip = 0; /* No hexgrip. */ else if (strlen (hexgrp) == 2*KEYGRIP_LEN) mem2str (kpi->keygrip, hexgrp, sizeof kpi->keygrip); else { err = gpg_error (GPG_ERR_INV_DATA); goto leave; } if (!*keyref) { err = gpg_error (GPG_ERR_INV_DATA); goto leave; } kpi->idstr = xtrystrdup (keyref); if (!kpi->idstr) { err = gpg_error_from_syserror (); goto leave; } /* Parse and set the usage. */ for (; *usage; usage++) { switch (*usage) { case 's': kpi->usage |= GCRY_PK_USAGE_SIGN; break; case 'c': kpi->usage |= GCRY_PK_USAGE_CERT; break; case 'a': kpi->usage |= GCRY_PK_USAGE_AUTH; break; case 'e': kpi->usage |= GCRY_PK_USAGE_ENCR; break; } } kpi->keytime = keytime; /* Append to the list. */ *parm->kpinfo_tail = kpi; parm->kpinfo_tail = &kpi->next; kpi = NULL; } leave: free_keypair_info (kpi); xfree (line_buffer); return err; } /* Read the keypairinfo lines of the current card directly from * scdaemon. The list is returned as a string made up of the keygrip, * a space and the keyref. The flags of the string carry the usage * bits. If KEYREF is not NULL, only a single string is returned * which matches the given keyref. */ gpg_error_t agent_scd_keypairinfo (ctrl_t ctrl, const char *keyref, keypair_info_t *r_list) { gpg_error_t err; struct keypairinfo_cb_parm_s parm; struct default_inq_parm_s inq_parm; char line[ASSUAN_LINELENGTH]; *r_list = NULL; err= start_agent (ctrl, 1); if (err) return err; memset (&inq_parm, 0, sizeof inq_parm); inq_parm.ctx = agent_ctx; parm.kpinfo = NULL; parm.kpinfo_tail = &parm.kpinfo; if (keyref) snprintf (line, DIM(line), "SCD READKEY --info-only %s", keyref); else snprintf (line, DIM(line), "SCD LEARN --keypairinfo"); err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inq_parm, scd_keypairinfo_status_cb, &parm); if (!err && !parm.kpinfo) err = gpg_error (GPG_ERR_NO_DATA); if (err) free_keypair_info (parm.kpinfo); else *r_list = parm.kpinfo; return err; } /* Send an APDU to the current card. On success the status word is * stored at R_SW unless R_SQ is NULL. With HEXAPDU being NULL only a * RESET command is send to scd. HEXAPDU may also be one of theseo * special strings: * * "undefined" :: Send the command "SCD SERIALNO undefined" * "lock" :: Send the command "SCD LOCK --wait" * "trylock" :: Send the command "SCD LOCK" * "unlock" :: Send the command "SCD UNLOCK" * "reset-keep-lock" :: Send the command "SCD RESET --keep-lock" * * Used by: * card-util.c */ 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, "reset-keep-lock")) { err = assuan_transact (agent_ctx, "SCD RESET --keep-lock", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "lock")) { err = assuan_transact (agent_ctx, "SCD LOCK --wait", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "trylock")) { err = assuan_transact (agent_ctx, "SCD LOCK", NULL, NULL, NULL, NULL, NULL, NULL); } else if (!strcmp (hexapdu, "unlock")) { err = assuan_transact (agent_ctx, "SCD UNLOCK", 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_keytotpm (ctrl_t ctrl, const char *hexgrip) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; snprintf(line, DIM(line), "KEYTOTPM %s\n", hexgrip); if (strchr (hexgrip, ',')) { log_error ("storing a part of a dual key is not yet supported\n"); return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } rc = start_agent (ctrl, 0); if (rc) return rc; parm.ctx = agent_ctx; parm.ctrl = ctrl; rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); if (rc) log_log (GPGRT_LOGLVL_ERROR, _("error from TPM: %s\n"), gpg_strerror (rc)); return rc; } /* Used by: * card_store_subkey * card_store_key_with_backup */ int agent_keytocard (const char *hexgrip, int keyno, int force, const char *serialno, const char *timestamp, const char *ecdh_param_str) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); if (strchr (hexgrip, ',')) { log_error ("storing a part of a dual key is not yet supported\n"); return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } snprintf (line, DIM(line), "KEYTOCARD %s%s %s OPENPGP.%d %s%s%s", force?"--force ": "", hexgrip, serialno, keyno, timestamp, ecdh_param_str? " ":"", ecdh_param_str? ecdh_param_str:""); 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); status_sc_op_failure (rc); return rc; } /* Object used with the agent_scd_getattr_one. */ struct getattr_one_parm_s { const char *keyword; /* Keyword to look for. */ char *data; /* Malloced and unescaped data. */ gpg_error_t err; /* Error code or 0 on success. */ }; /* Callback for agent_scd_getattr_one. */ static gpg_error_t getattr_one_status_cb (void *opaque, const char *line) { struct getattr_one_parm_s *parm = opaque; const char *s; if (parm->data) return 0; /* We want only the first occurrence. */ if ((s=has_leading_keyword (line, parm->keyword))) { parm->data = percent_plus_unescape (s, 0xff); if (!parm->data) parm->err = gpg_error_from_syserror (); } return 0; } /* Simplified version of agent_scd_getattr. This function returns * only the first occurrence of the attribute NAME and stores it at * R_VALUE. A nul in the result is silennly replaced by 0xff. On * error NULL is stored at R_VALUE. */ gpg_error_t agent_scd_getattr_one (const char *name, char **r_value) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inqparm; struct getattr_one_parm_s parm; *r_value = NULL; if (!*name) return gpg_error (GPG_ERR_INV_VALUE); memset (&inqparm, 0, sizeof inqparm); inqparm.ctx = agent_ctx; memset (&parm, 0, sizeof parm); parm.keyword = name; /* 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); err = start_agent (NULL, 1); if (err) return err; err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inqparm, getattr_one_status_cb, &parm); if (!err && parm.err) err = parm.err; else if (!err && !parm.data) err = gpg_error (GPG_ERR_NO_DATA); if (!err) *r_value = parm.data; else xfree (parm.data); return err; } /* 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. * * Used by: * build_sk_list * enum_secret_keys * get_signature_count * card-util.c * generate_keypair (KEY-ATTR) * card_store_key_with_backup (SERIALNO) * generate_card_subkeypair (KEY-ATTR) */ 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); if (!rc && !strcmp (name, "KEY-FPR")) { /* Let the agent create the shadow keys if not yet done. */ if (info->fpr1len) assuan_transact (agent_ctx, "READKEY --card --no-data -- $SIGNKEYID", NULL, NULL, NULL, NULL, NULL, NULL); if (info->fpr2len) assuan_transact (agent_ctx, "READKEY --card --no-data -- $ENCRKEYID", NULL, NULL, NULL, NULL, NULL, NULL); } return rc; } /* Send an setattr command to the SCdaemon. * Used by: * card-util.c */ gpg_error_t agent_scd_setattr (const char *name, const void *value_arg, size_t valuelen) { gpg_error_t err; const unsigned char *value = value_arg; char line[ASSUAN_LINELENGTH]; char *p; struct default_inq_parm_s parm; memset (&parm, 0, sizeof parm); 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; err = start_agent (NULL, 1); if (!err) { parm.ctx = agent_ctx; err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm, NULL, NULL); } status_sc_op_failure (err); return err; } /* 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. * Used by: * card-util.c */ 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; } /* 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. * Used by: * gen_card_key */ 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. With DEMAND the active * card is switched to the card with that serialno. * Used by: * card-util.c * build_sk_list * enum_secret_keys */ int agent_scd_serialno (char **r_serialno, const char *demand) { int err; char *serialno = NULL; char line[ASSUAN_LINELENGTH]; if (r_serialno) *r_serialno = NULL; 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; } if (r_serialno) *r_serialno = serialno; else xfree (serialno); return 0; } /* Send a READCERT command to the SCdaemon. * Used by: * card-util.c */ 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; } /* Callback for the agent_scd_readkey function. */ static gpg_error_t readkey_status_cb (void *opaque, const char *line) { u32 *keytimep = opaque; gpg_error_t err = 0; const char *args; char *line_buffer = NULL; /* FIXME: Get that info from the KEYPAIRINFO line. */ if ((args = has_leading_keyword (line, "KEYPAIRINFO")) && !*keytimep) { /* The format of such a line is: * KEYPAIRINFO [usage] [keytime] * * Note that we use only the first valid KEYPAIRINFO line. More * lines are possible if a second card carries the same key. */ const char *fields[4]; int nfields; time_t atime; line_buffer = xtrystrdup (line); if (!line_buffer) { err = gpg_error_from_syserror (); goto leave; } if ((nfields = split_fields (line_buffer, fields, DIM (fields))) < 4) goto leave; /* not enough args - ignore */ if (nfields > 3) { atime = parse_timestamp (fields[3], NULL); if (atime == (time_t)(-1)) atime = 0; *keytimep = atime; } else *keytimep = 0; } leave: xfree (line_buffer); return err; } /* This is a variant of agent_readkey which sends a READKEY command * directly Scdaemon. On success a new s-expression is stored at * R_RESULT. If R_KEYTIME is not NULL the key cresation time of an * OpenPGP card is stored there - if that is not known 0 is stored. * In the latter case it is allowed to pass NULL for R_RESULT. */ gpg_error_t agent_scd_readkey (ctrl_t ctrl, const char *keyrefstr, gcry_sexp_t *r_result, u32 *r_keytime) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; membuf_t data; unsigned char *buf; size_t len, buflen; struct default_inq_parm_s dfltparm; u32 keytime; memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctx = agent_ctx; if (r_result) *r_result = NULL; if (r_keytime) *r_keytime = 0; err = start_agent (ctrl, 1); if (err) return err; init_membuf (&data, 1024); snprintf (line, DIM(line), "SCD READKEY --info%s -- %s", r_result? "":"-only", keyrefstr); keytime = 0; err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, readkey_status_cb, &keytime); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &buflen); if (!buf) return gpg_error_from_syserror (); if (r_result) err = gcry_sexp_new (r_result, buf, buflen, 0); else err = 0; xfree (buf); if (!err && r_keytime) *r_keytime = keytime; return err; } 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 a list of currently available cards. * Used by: * card-util.c * skclist.c */ 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 | FLAG_FOR_CARD_SUPPRESS_ERRORS); 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; } /* Make the app APPNAME the one on the card. This is sometimes * required to make sure no other process has switched a card to * another application. The only useful APPNAME is "openpgp". */ gpg_error_t agent_scd_switchapp (const char *appname) { int err; char line[ASSUAN_LINELENGTH]; if (appname && !*appname) appname = NULL; err = start_agent (NULL, (1 | FLAG_FOR_CARD_SUPPRESS_ERRORS)); if (err) return err; snprintf (line, DIM(line), "SCD SWITCHAPP --%s%s", appname? " ":"", appname? appname:""); return assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); } struct card_keyinfo_parm_s { int error; keypair_info_t list; }; /* Callback function for agent_card_keylist. */ static gpg_error_t card_keyinfo_cb (void *opaque, const char *line) { gpg_error_t err = 0; struct card_keyinfo_parm_s *parm = opaque; const char *keyword = line; int keywordlen; keypair_info_t keyinfo = NULL; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 7 && !memcmp (keyword, "KEYINFO", keywordlen)) { const char *s; int n; keypair_info_t *l_p = &parm->list; while ((*l_p)) l_p = &(*l_p)->next; keyinfo = xtrycalloc (1, sizeof *keyinfo); if (!keyinfo) goto alloc_error; for (n=0,s=line; hexdigitp (s); s++, n++) ; if (n != 40) goto parm_error; memcpy (keyinfo->keygrip, line, 40); keyinfo->keygrip[40] = 0; line = s; if (!*line) goto parm_error; while (spacep (line)) line++; if (*line++ != 'T') goto parm_error; if (!*line) goto parm_error; while (spacep (line)) line++; for (n=0,s=line; hexdigitp (s); s++, n++) ; if (!n) goto parm_error; keyinfo->serialno = xtrymalloc (n+1); if (!keyinfo->serialno) goto alloc_error; memcpy (keyinfo->serialno, line, n); keyinfo->serialno[n] = 0; line = s; if (!*line) goto parm_error; while (spacep (line)) line++; if (!*line) goto parm_error; keyinfo->idstr = xtrystrdup (line); if (!keyinfo->idstr) goto alloc_error; *l_p = keyinfo; } return err; alloc_error: xfree (keyinfo); if (!parm->error) parm->error = gpg_error_from_syserror (); return 0; parm_error: xfree (keyinfo); if (!parm->error) parm->error = gpg_error (GPG_ERR_ASS_PARAMETER); return 0; } /* Free a keypair info list. */ void free_keypair_info (keypair_info_t l) { keypair_info_t l_next; for (; l; l = l_next) { l_next = l->next; xfree (l->serialno); xfree (l->idstr); xfree (l); } } /* Call the scdaemon to check if a key of KEYGRIP is available, or retrieve list of available keys on cards. With CAP, we can limit keys with specified capability. On success, the allocated structure is stored at RESULT. On error, an error code is returned and NULL is stored at RESULT. */ gpg_error_t agent_scd_keyinfo (const char *keygrip, int cap, keypair_info_t *result) { int err; struct card_keyinfo_parm_s parm; char line[ASSUAN_LINELENGTH]; char *list_option; *result = NULL; switch (cap) { case 0: list_option = "--list"; break; case GCRY_PK_USAGE_SIGN: list_option = "--list=sign"; break; case GCRY_PK_USAGE_ENCR: list_option = "--list=encr"; break; case GCRY_PK_USAGE_AUTH: list_option = "--list=auth"; break; default: return gpg_error (GPG_ERR_INV_VALUE); } memset (&parm, 0, sizeof parm); snprintf (line, sizeof line, "SCD KEYINFO %s", keygrip ? keygrip : list_option); err = start_agent (NULL, 1 | FLAG_FOR_CARD_SUPPRESS_ERRORS); if (err) return err; err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, card_keyinfo_cb, &parm); if (!err && parm.error) err = parm.error; if (!err) *result = parm.list; else free_keypair_info (parm.list); return err; } /* 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. * Used by: * card-util.c */ 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. * Used by: * card-util.c */ 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; } /* 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 error code returned. Only called from passphrase.c:passphrase_get - see there for more comments on this ugly API. */ gpg_error_t agent_get_passphrase (const char *cache_id, const char *err_msg, const char *prompt, const char *desc_msg, int newsymkey, 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; int have_newsymkey, wasconf; 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); have_newsymkey = !(assuan_transact (agent_ctx, "GETINFO cmd_has_option GET_PASSPHRASE newsymkey", NULL, NULL, NULL, NULL, NULL, NULL)); 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; /* CHECK && REPEAT or NEWSYMKEY is here an indication that a new * passphrase for symmetric encryption is requested; if the agent * supports this we enable the modern API by also passing --newsymkey. */ snprintf (line, DIM(line), "GET_PASSPHRASE --data --repeat=%d%s%s -- %s %s %s %s", repeat, ((repeat && check) || newsymkey)? " --check":"", (have_newsymkey && newsymkey)? " --newsymkey":"", 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); wasconf = assuan_get_flag (agent_ctx, ASSUAN_CONFIDENTIAL); assuan_begin_confidential (agent_ctx); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); if (!wasconf) assuan_end_confidential (agent_ctx); 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. On error * print a warning and return a default value. */ unsigned long agent_get_s2k_count (void) { gpg_error_t err; membuf_t data; char *buf; unsigned long count = 0; err = start_agent (NULL, 0); if (err) goto leave; 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 { count = strtoul (buf, NULL, 10); xfree (buf); } } leave: if (err || count < 65536) { /* Don't print an error if an older agent is used. */ if (err && gpg_err_code (err) != GPG_ERR_ASS_PARAMETER) log_error (_("problem with the agent: %s\n"), gpg_strerror (err)); /* Default to 65536 which was used up to 2.0.13. */ count = 65536; } return count; } struct keyinfo_data_parm_s { char *serialno; int is_smartcard; int passphrase_cached; int cleartext; int card_available; }; static gpg_error_t keyinfo_status_cb (void *opaque, const char *line) { struct keyinfo_data_parm_s *data = opaque; char *s; if ((s = has_leading_keyword (line, "KEYINFO")) && data) { /* Parse the arguments: * 0 1 2 3 4 5 * * * 6 7 8 * */ const char *fields[9]; if (split_fields (s, fields, DIM (fields)) == 9) { data->is_smartcard = (fields[1][0] == 'T'); if (data->is_smartcard && !data->serialno && strcmp (fields[2], "-")) data->serialno = xtrystrdup (fields[2]); /* '1' for cached */ data->passphrase_cached = (fields[4][0] == '1'); /* 'P' for protected, 'C' for clear */ data->cleartext = (fields[5][0] == 'C'); /* 'A' for card is available */ data->card_available = (fields[8][0] == 'A'); } } return 0; } /* Ask the agent whether a secret key for the given public key is * available. Returns 0 if not available. Bigger value is preferred. * Will never return a value less than 0. Defined return values are: * 0 := No key or error * 1 := Key available * 2 := Key available on a smartcard * 3 := Key available and passphrase cached * 4 := Key available on current smartcard */ int agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *hexgrip, *p; struct keyinfo_data_parm_s keyinfo; int result, result2; memset (&keyinfo, 0, sizeof keyinfo); err = start_agent (ctrl, 0); if (err) return 0; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) return 0; if ((p=strchr (hexgrip, ','))) *p++ = 0; snprintf (line, sizeof line, "KEYINFO %s", hexgrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); xfree (keyinfo.serialno); if (err) result = 0; else if (keyinfo.card_available) result = 4; else if (keyinfo.passphrase_cached) result = 3; else if (keyinfo.is_smartcard) result = 2; else result = 1; if (!p) { xfree (hexgrip); return result; /* Not a dual algo - we are ready. */ } /* Now check the second keygrip. */ memset (&keyinfo, 0, sizeof keyinfo); snprintf (line, sizeof line, "KEYINFO %s", p); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); xfree (keyinfo.serialno); if (err) result2 = 0; else if (keyinfo.card_available) result2 = 4; else if (keyinfo.passphrase_cached) result2 = 3; else if (keyinfo.is_smartcard) result2 = 2; else result2 = 1; xfree (hexgrip); if (result == result2) return result; /* Both keys have the same status. */ else if (!result && result2) return 0; /* Only first key available - return no key. */ else if (result && !result2) return 0; /* Only second key not availabale - return no key. */ else if (result == 4 || result == 2) return result; /* First key on card - don't care where the second is. */ else return result; } /* 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; /* (always zero in secret_keygrips mode) */ unsigned char grip[KEYGRIP_LEN]; unsigned char grip2[KEYGRIP_LEN]; int grip2_valid; const unsigned char *s; unsigned int n; err = start_agent (ctrl, 0); if (err) return err; /* If we have not yet issued a "HAVEKEY --list" do that now. We use * a more or less arbitray limit of 1000 keys. */ if (ctrl && !ctrl->secret_keygrips && !ctrl->no_more_secret_keygrips) { membuf_t data; init_membuf (&data, 4096); err = assuan_transact (agent_ctx, "HAVEKEY --list=1000", put_membuf_cb, &data, NULL, NULL, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { ctrl->secret_keygrips = get_membuf (&data, &ctrl->secret_keygrips_len); if (!ctrl->secret_keygrips) err = gpg_error_from_syserror (); if ((ctrl->secret_keygrips_len % 20)) { err = gpg_error (GPG_ERR_INV_DATA); xfree (ctrl->secret_keygrips); ctrl->secret_keygrips = NULL; } } if (err) { if (opt.quiet) log_info ("problem with fast path key listing: %s - ignored\n", gpg_strerror (err)); err = 0; } /* We want to do this only once. */ ctrl->no_more_secret_keygrips = 1; } 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 (ctrl && ctrl->secret_keygrips) { /* We got an array with all secret keygrips. Check this. */ err = keygrip_from_pk (node->pkt->pkt.public_key, grip, 0); if (err) return err; err = keygrip_from_pk (node->pkt->pkt.public_key, grip2, 1); if (err && gpg_err_code (err) != GPG_ERR_FALSE) return err; grip2_valid = !err; for (s=ctrl->secret_keygrips, n = 0; n < ctrl->secret_keygrips_len; s += 20, n += 20) { if (!memcmp (s, grip, 20)) return 0; if (grip2_valid && !memcmp (s, grip2, 20)) return 0; } err = gpg_error (GPG_ERR_NO_SECKEY); /* Keep on looping over the keyblock. Never bump nkeys. */ } else { if (nkeys && ((p - line) + 4*KEYGRIP_LEN+1+1) > (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, 0); if (err) return err; *p++ = ' '; bin2hex (grip, 20, p); p += 40; nkeys++; err = keygrip_from_pk (node->pkt->pkt.public_key, grip2, 1); if (err && gpg_err_code (err) != GPG_ERR_FALSE) return err; if (!err) /* Add the second keygrip from dual algos. */ { *p++ = ' '; bin2hex (grip2, 20, p); p += 40; nkeys++; } } } if (!err && nkeys) err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); return err; } /* 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; const char *s; memset (&keyinfo, 0,sizeof keyinfo); *r_serialno = NULL; err = start_agent (ctrl, 0); if (err) return err; /* FIXME: Support dual keys. Maybe under the assumption that the * first key might be on a card. */ if (!hexkeygrip) return gpg_error (GPG_ERR_INV_VALUE); s = strchr (hexkeygrip, ','); if (!s) s = hexkeygrip + strlen (hexkeygrip); if (s - hexkeygrip != 40) return gpg_error (GPG_ERR_INV_VALUE); /* Note that for a dual algo we only get info for the first key. * FIXME: We need to see how we can show the status of the second * key in a key listing. */ snprintf (line, DIM(line), "KEYINFO %.40s", 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. TIMESTAMP is the creation time of the key or zero. */ 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, time_t timestamp, 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 timestamparg[16 + 16]; /* The 2nd 16 is sizeof(gnupg_isotime_t) */ 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; /* Do not use our cache of secret keygrips anymore - this command * would otherwise requiring to update that cache. */ if (ctrl && ctrl->secret_keygrips) { xfree (ctrl->secret_keygrips); ctrl->secret_keygrips = 0; } if (timestamp) { strcpy (timestamparg, " --timestamp="); epoch2isotime (timestamparg+13, timestamp); } else *timestamparg = 0; 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%s", *timestamparg? timestamparg : "", 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. * Values from FROMCARD: * 0 - Standard * 1 - The key is read from the current card * via the agent and a stub file is created. */ 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; if (fromcard) snprintf (line, DIM(line), "READKEY --card -- %s", hexkeygrip); else snprintf (line, DIM(line), "READKEY -- %s", 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; + const char *keygrip2 = NULL; 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) + if (!keygrip || !s_ciphertext || !r_buf || !r_buflen || !r_padding) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; *r_padding = -1; + /* Parse the keygrip in case of a dual algo. */ + keygrip2 = strchr (keygrip, ','); + if (!keygrip2) + keygrip2 = keygrip + strlen (keygrip); + if (keygrip2 - keygrip != 40) + return gpg_error (GPG_ERR_INV_VALUE); + if (*keygrip2) + { + keygrip2++; + if (strlen (keygrip2) != 40) + return gpg_error (GPG_ERR_INV_VALUE); + } + + 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); + snprintf (line, sizeof line, "SETKEY %.40s", keygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; + if (*keygrip2) + { + snprintf (line, sizeof line, "SETKEY --another %.40s", keygrip2); + 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", + err = assuan_transact (agent_ctx, + *keygrip2? "PKDECRYPT --kem=PQC-PGP":"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; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); if (len == 0 || *buf != '(') { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } if (len < 12 || memcmp (buf, "(5:value", 8) ) /* "(5:valueN:D)" */ { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } 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. */ 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, u32 timestamp) { gpg_error_t err; struct import_key_parm_s parm; struct cache_nonce_parm_s cn_parm; char timestamparg[16 + 16]; /* The 2nd 16 is sizeof(gnupg_isotime_t) */ 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; /* Do not use our cache of secret keygrips anymore - this command * would otherwise requiring to update that cache. */ if (ctrl && ctrl->secret_keygrips) { xfree (ctrl->secret_keygrips); ctrl->secret_keygrips = 0; } if (timestamp) { strcpy (timestamparg, " --timestamp="); epoch2isotime (timestamparg+13, timestamp); } else *timestamparg = 0; 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%s", *timestamparg? timestamparg : "", 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 instead MODE1003 is not zero the raw gpg-agent private key format is requested (either protected or unprotected). 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, int mode1003, 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; /* Check that the gpg-agent supports the --mode1003 option. */ if (mode1003 && assuan_transact (agent_ctx, "GETINFO cmd_has_option EXPORT_KEY mode1003", NULL, NULL, NULL, NULL, NULL, NULL)) return gpg_error (GPG_ERR_NOT_SUPPORTED); 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", mode1003? "--mode1003" : 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; } /* Status callback for handling confirmation. */ static gpg_error_t confirm_status_cb (void *opaque, const char *line) { struct confirm_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "SETDESC"))) { xfree (parm->desc); parm->desc = unescape_status_string (s); } else if ((s = has_leading_keyword (line, "SETOK"))) { xfree (parm->ok); parm->ok = unescape_status_string (s); } else if ((s = has_leading_keyword (line, "SETNOTOK"))) { xfree (parm->notok); parm->notok = unescape_status_string (s); } 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; struct confirm_parm_s confirm_parm; memset (&confirm_parm, 0, sizeof confirm_parm); memset (&dfltparm, 0, sizeof dfltparm); dfltparm.ctrl = ctrl; dfltparm.confirm = &confirm_parm; 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; } /* FIXME: Shall we add support to DELETE_KEY for dual keys? */ snprintf (line, DIM(line), "DELETE_KEY%s %s", force? " --force":"", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &dfltparm, confirm_status_cb, &confirm_parm); xfree (confirm_parm.desc); xfree (confirm_parm.ok); xfree (confirm_parm.notok); 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; } /* Enable or disable the ephemeral mode. In ephemeral mode keys are * created,searched and used in a per-session key store and not in the * on-disk file. Set ENABLE to 1 to enable this mode, to 0 to disable * this mode and to -1 to only query the current mode. If R_PREVIOUS * is given the previously used state of the ephemeral mode is stored * at that address. */ gpg_error_t agent_set_ephemeral_mode (ctrl_t ctrl, int enable, int *r_previous) { gpg_error_t err; err = start_agent (ctrl, 0); if (err) goto leave; if (r_previous) { err = assuan_transact (agent_ctx, "GETINFO ephemeral", NULL, NULL, NULL, NULL, NULL, NULL); if (!err) *r_previous = 1; else if (gpg_err_code (err) == GPG_ERR_FALSE) *r_previous = 0; else goto leave; } /* Skip setting if we are only querying or if the mode is already set. */ if (enable == -1 || (r_previous && !!*r_previous == !!enable)) err = 0; else err = assuan_transact (agent_ctx, enable? "OPTION ephemeral=1" : "OPTION ephemeral=0", NULL, NULL, NULL, NULL, NULL, NULL); leave: 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/ecdh.c b/g10/ecdh.c index 4938e419d..279508bec 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -1,608 +1,608 @@ /* ecdh.c - ECDH public key operations used in public key glue code * Copyright (C) 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "pkglue.h" #include "main.h" #include "options.h" /* A table with the default KEK parameters used by GnuPG. */ static const struct { unsigned int qbits; int openpgp_hash_id; /* KEK digest algorithm. */ int openpgp_cipher_id; /* KEK cipher algorithm. */ } kek_params_table[] = /* Note: Must be sorted by ascending values for QBITS. */ { { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES }, { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 }, /* Note: 528 is 521 rounded to the 8 bit boundary */ { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 } }; /* Return KEK parameters as an opaque MPI The caller must free the returned value. Returns NULL and sets ERRNO on error. */ gcry_mpi_t pk_ecdh_default_params (unsigned int qbits) { byte kek_params[4] = { 3, /* Number of bytes to follow. */ 1 /* Version for KDF+AESWRAP. */ }; int i; /* Search for matching KEK parameter. Defaults to the strongest possible choices. Performance is not an issue here, only interoperability. */ for (i=0; i < DIM (kek_params_table); i++) { if (kek_params_table[i].qbits >= qbits || i+1 == DIM (kek_params_table)) { kek_params[2] = kek_params_table[i].openpgp_hash_id; kek_params[3] = kek_params_table[i].openpgp_cipher_id; break; } } log_assert (i < DIM (kek_params_table)); if (DBG_CRYPTO) log_printhex (kek_params, sizeof(kek_params), "ECDH KEK params are"); return gcry_mpi_set_opaque_copy (NULL, kek_params, 4 * 8); } /* Extract xcomponent from the point SHARED. POINT_NBYTES is the size to represent an EC point which is determined by the public key. SECRET_X_SIZE is the size of x component to represent an integer which is determined by the curve. */ static gpg_error_t extract_secret_x (byte **r_secret_x, const char *shared, size_t nshared, size_t point_nbytes, size_t secret_x_size) { byte *secret_x; *r_secret_x = NULL; /* Extract X from the result. It must be in the format of: 04 || X || Y 40 || X 41 || X Since it may come with the prefix, the size of point is larger than or equals to the size of an integer X. We also better check that the provided shared point is not larger than the size needed to represent the point. */ if (point_nbytes < secret_x_size) return gpg_error (GPG_ERR_BAD_DATA); if (point_nbytes < nshared) return gpg_error (GPG_ERR_BAD_DATA); /* Extract x component of the shared point: this is the actual shared secret. */ secret_x = xtrymalloc_secure (point_nbytes); if (!secret_x) return gpg_error_from_syserror (); memcpy (secret_x, shared, nshared); /* Wrangle the provided point unless only the x-component w/o any * prefix was provided. */ if (nshared != secret_x_size) { /* Remove the prefix. */ if ((point_nbytes & 1)) memmove (secret_x, secret_x+1, secret_x_size); /* Clear the rest of data. */ if (point_nbytes - secret_x_size) memset (secret_x+secret_x_size, 0, point_nbytes-secret_x_size); } if (DBG_CRYPTO) log_printhex (secret_x, secret_x_size, "ECDH shared secret X is:"); *r_secret_x = secret_x; return 0; } /* Build KDF parameters */ /* RFC 6637 defines the KDF parameters and its encoding in Section 8. EC DH Algorighm (ECDH). Since it was written for v4 key, it said "20 octets representing a recipient encryption subkey or a master key fingerprint". For v5 key, it is considered "adequate" (in terms of NIST SP 800 56A, see 5.8.2 FixedInfo) to use the first 20 octets of its 32 octets fingerprint. */ static gpg_error_t build_kdf_params (unsigned char kdf_params[256], size_t *r_size, gcry_mpi_t *pkey, const byte pk_fp[MAX_FINGERPRINT_LEN]) { IOBUF obuf; gpg_error_t err; *r_size = 0; obuf = iobuf_temp(); if (!obuf) return gpg_error_from_syserror (); /* variable-length field 1, curve name OID */ err = gpg_mpi_write_opaque_nohdr (obuf, pkey[0]); /* fixed-length field 2 */ iobuf_put (obuf, PUBKEY_ALGO_ECDH); /* variable-length field 3, KDF params */ err = (err ? err : gpg_mpi_write_opaque_nohdr (obuf, pkey[2])); /* fixed-length field 4 */ iobuf_write (obuf, "Anonymous Sender ", 20); /* fixed-length field 5, recipient fp (or first 20 octets of fp) */ iobuf_write (obuf, pk_fp, 20); if (!err) *r_size = iobuf_temp_to_buffer (obuf, kdf_params, 256); iobuf_close (obuf); if (!err) { if (DBG_CRYPTO) log_printhex (kdf_params, *r_size, "ecdh KDF message params are:"); } return err; } /* Derive KEK with KEK_SIZE into the memory at SECRET_X. */ static gpg_error_t derive_kek (size_t kek_size, int kdf_hash_algo, byte *secret_x, int secret_x_size, const unsigned char *kdf_params, size_t kdf_params_size) { gpg_error_t err; #if 0 /* GCRYPT_VERSION_NUMBER >= 0x010b00 */ /* * Experimental: We will remove this if/endif-conditional * compilation when we update NEED_LIBGCRYPT_VERSION to 1.11.0. */ gcry_kdf_hd_t hd; unsigned long param[1]; param[0] = kek_size; err = gcry_kdf_open (&hd, GCRY_KDF_ONESTEP_KDF, kdf_hash_algo, param, 1, secret_x, secret_x_size, NULL, 0, NULL, 0, kdf_params, kdf_params_size); if (!err) { gcry_kdf_compute (hd, NULL); gcry_kdf_final (hd, kek_size, secret_x); gcry_kdf_close (hd); /* Clean the tail before returning. */ memset (secret_x+kek_size, 0, secret_x_size - kek_size); } #else gcry_md_hd_t h; log_assert( gcry_md_get_algo_dlen (kdf_hash_algo) >= 32 ); err = gcry_md_open (&h, kdf_hash_algo, 0); if (err) { log_error ("gcry_md_open failed for kdf_hash_algo %d: %s", kdf_hash_algo, gpg_strerror (err)); return err; } gcry_md_write(h, "\x00\x00\x00\x01", 4); /* counter = 1 */ gcry_md_write(h, secret_x, secret_x_size); /* x of the point X */ gcry_md_write(h, kdf_params, kdf_params_size); /* KDF parameters */ gcry_md_final (h); memcpy (secret_x, gcry_md_read (h, kdf_hash_algo), kek_size); gcry_md_close (h); /* Clean the tail before returning. */ memset (secret_x+kek_size, 0, secret_x_size - kek_size); #endif if (DBG_CRYPTO) log_printhex (secret_x, kek_size, "ecdh KEK is:"); return err; } /* Prepare ECDH using SHARED, PK_FP fingerprint, and PKEY array. Returns the cipher handle in R_HD, which needs to be closed by the caller. */ static gpg_error_t prepare_ecdh_with_shared_point (const char *shared, size_t nshared, const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t *pkey, gcry_cipher_hd_t *r_hd) { gpg_error_t err; byte *secret_x; int secret_x_size; unsigned int nbits; const unsigned char *kek_params; size_t kek_params_size; int kdf_hash_algo; int kdf_encr_algo; unsigned char kdf_params[256]; size_t kdf_params_size; size_t kek_size; gcry_cipher_hd_t hd; *r_hd = NULL; if (!gcry_mpi_get_flag (pkey[2], GCRYMPI_FLAG_OPAQUE)) return gpg_error (GPG_ERR_BUG); kek_params = gcry_mpi_get_opaque (pkey[2], &nbits); kek_params_size = (nbits+7)/8; if (DBG_CRYPTO) log_printhex (kek_params, kek_params_size, "ecdh KDF params:"); /* Expect 4 bytes 03 01 hash_alg symm_alg. */ if (kek_params_size != 4 || kek_params[0] != 3 || kek_params[1] != 1) return gpg_error (GPG_ERR_BAD_PUBKEY); kdf_hash_algo = kek_params[2]; kdf_encr_algo = kek_params[3]; if (DBG_CRYPTO) log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n", openpgp_md_algo_name (kdf_hash_algo), openpgp_cipher_algo_name (kdf_encr_algo)); if (kdf_hash_algo != GCRY_MD_SHA256 && kdf_hash_algo != GCRY_MD_SHA384 && kdf_hash_algo != GCRY_MD_SHA512) return gpg_error (GPG_ERR_BAD_PUBKEY); if (kdf_encr_algo != CIPHER_ALGO_AES && kdf_encr_algo != CIPHER_ALGO_AES192 && kdf_encr_algo != CIPHER_ALGO_AES256) return gpg_error (GPG_ERR_BAD_PUBKEY); kek_size = gcry_cipher_get_algo_keylen (kdf_encr_algo); if (kek_size > gcry_md_get_algo_dlen (kdf_hash_algo)) return gpg_error (GPG_ERR_BAD_PUBKEY); /* Build kdf_params. */ err = build_kdf_params (kdf_params, &kdf_params_size, pkey, pk_fp); if (err) return err; nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); if (!nbits) return gpg_error (GPG_ERR_TOO_SHORT); secret_x_size = (nbits+7)/8; if (kek_size > secret_x_size) return gpg_error (GPG_ERR_BAD_PUBKEY); err = extract_secret_x (&secret_x, shared, nshared, /* pkey[1] is the public point */ (mpi_get_nbits (pkey[1])+7)/8, secret_x_size); if (err) return err; /*** We have now the shared secret bytes in secret_x. ***/ /* At this point we are done with PK encryption and the rest of the * function uses symmetric key encryption techniques to protect the * input DATA. The following two sections will simply replace * current secret_x with a value derived from it. This will become * a KEK. */ /* Derive a KEK (key wrapping key) using SECRET_X and KDF_PARAMS. */ err = derive_kek (kek_size, kdf_hash_algo, secret_x, secret_x_size, kdf_params, kdf_params_size); if (err) { xfree (secret_x); return err; } /* And, finally, aeswrap with key secret_x. */ err = gcry_cipher_open (&hd, kdf_encr_algo, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) { log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err)); xfree (secret_x); return err; } err = gcry_cipher_setkey (hd, secret_x, kek_size); xfree (secret_x); secret_x = NULL; if (err) { gcry_cipher_close (hd); log_error ("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (err)); } else *r_hd = hd; return err; } /* Encrypts DATA using a key derived from the ECC shared point SHARED using the FIPS SP 800-56A compliant method key_derivation+key_wrapping. PKEY is the public key and PK_FP the fingerprint of this public key. On success the result is stored at R_RESULT; on failure NULL is stored at R_RESULT and an error code returned. */ gpg_error_t pk_ecdh_encrypt_with_shared_point (const char *shared, size_t nshared, const byte pk_fp[MAX_FINGERPRINT_LEN], const byte *data, size_t ndata, gcry_mpi_t *pkey, gcry_mpi_t *r_result) { gpg_error_t err; gcry_cipher_hd_t hd; byte *data_buf; int data_buf_size; gcry_mpi_t result; byte *in; *r_result = NULL; err = prepare_ecdh_with_shared_point (shared, nshared, pk_fp, pkey, &hd); if (err) return err; data_buf_size = ndata; if ((data_buf_size & 7) != 0) { log_error ("can't use a shared secret of %d bytes for ecdh\n", data_buf_size); gcry_cipher_close (hd); return gpg_error (GPG_ERR_BAD_DATA); } data_buf = xtrymalloc_secure( 1 + 2*data_buf_size + 8); if (!data_buf) { err = gpg_error_from_syserror (); gcry_cipher_close (hd); return err; } in = data_buf+1+data_buf_size+8; /* Write data MPI into the end of data_buf. data_buf is size aeswrap data. */ memcpy (in, data, ndata); if (DBG_CRYPTO) log_printhex (in, data_buf_size, "ecdh encrypting :"); err = gcry_cipher_encrypt (hd, data_buf+1, data_buf_size+8, in, data_buf_size); memset (in, 0, data_buf_size); gcry_cipher_close (hd); if (err) { log_error ("ecdh failed in gcry_cipher_encrypt: %s\n", gpg_strerror (err)); xfree (data_buf); return err; } data_buf[0] = data_buf_size+8; if (DBG_CRYPTO) log_printhex (data_buf+1, data_buf[0], "ecdh encrypted to:"); result = gcry_mpi_set_opaque (NULL, data_buf, 8 * (1+data_buf[0])); if (!result) { err = gpg_error_from_syserror (); xfree (data_buf); log_error ("ecdh failed to create an MPI: %s\n", gpg_strerror (err)); return err; } *r_result = result; return err; } static gcry_mpi_t gen_k (unsigned nbits, int little_endian, int is_opaque) { gcry_mpi_t k; if (is_opaque) { unsigned char *p; size_t nbytes = (nbits+7)/8; p = gcry_random_bytes_secure (nbytes, GCRY_STRONG_RANDOM); if ((nbits % 8)) { if (little_endian) p[nbytes-1] &= ((1 << (nbits % 8)) - 1); else p[0] &= ((1 << (nbits % 8)) - 1); } k = gcry_mpi_set_opaque (NULL, p, nbits); return k; } k = gcry_mpi_snew (nbits); if (DBG_CRYPTO) log_debug ("choosing a random k of %u bits\n", nbits); gcry_mpi_randomize (k, nbits-1, GCRY_STRONG_RANDOM); if (DBG_CRYPTO) { unsigned char *buffer; if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, k)) BUG (); log_debug ("ephemeral scalar MPI #0: %s\n", buffer); gcry_free (buffer); } return k; } /* Generate an ephemeral key for the public ECDH key in PKEY. On success the generated key is stored at R_K; on failure NULL is stored at R_K and an error code returned. */ gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k) { unsigned int nbits; gcry_mpi_t k; int is_little_endian = 0; int require_opaque = 0; if (openpgp_oid_is_cv448 (pkey[0])) { is_little_endian = 1; require_opaque = 1; } *r_k = NULL; nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); if (!nbits) return gpg_error (GPG_ERR_TOO_SHORT); k = gen_k (nbits, is_little_endian, require_opaque); if (!k) BUG (); *r_k = k; return 0; } /* Perform ECDH decryption. */ int pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t data, const byte *shared, size_t nshared, gcry_mpi_t * skey) { gpg_error_t err; gcry_cipher_hd_t hd; size_t nbytes; byte *data_buf; int data_buf_size; const unsigned char *p; unsigned int nbits; *r_result = NULL; err = prepare_ecdh_with_shared_point (shared, nshared, sk_fp, skey, &hd); if (err) return err; p = gcry_mpi_get_opaque (data, &nbits); nbytes = (nbits+7)/8; data_buf_size = nbytes; - if ((data_buf_size & 7) != 1) + if ((data_buf_size & 7) != 1 || data_buf_size <= 1 + 8) { log_error ("can't use a shared secret of %d bytes for ecdh\n", data_buf_size); gcry_cipher_close (hd); return gpg_error (GPG_ERR_BAD_DATA); } /* The first octet is for length. It's longer than the result because of one additional block of AESWRAP. */ data_buf_size -= 1 + 8; data_buf = xtrymalloc_secure (data_buf_size); if (!data_buf) { err = gpg_error_from_syserror (); gcry_cipher_close (hd); return err; } if (!p) { xfree (data_buf); gcry_cipher_close (hd); return gpg_error (GPG_ERR_BAD_MPI); } if (p[0] != nbytes-1) { log_error ("ecdh inconsistent size\n"); xfree (data_buf); gcry_cipher_close (hd); return gpg_error (GPG_ERR_BAD_MPI); } if (DBG_CRYPTO) log_printhex (p+1, nbytes-1, "ecdh decrypting :"); err = gcry_cipher_decrypt (hd, data_buf, data_buf_size, p+1, nbytes-1); gcry_cipher_close (hd); if (err) { log_error ("ecdh failed in gcry_cipher_decrypt: %s\n", gpg_strerror (err)); xfree (data_buf); return err; } if (DBG_CRYPTO) log_printhex (data_buf, data_buf_size, "ecdh decrypted to :"); /* Padding is removed later. */ /* if (in[data_buf_size-1] > 8 ) */ /* { */ /* log_error ("ecdh failed at decryption: invalid padding." */ /* " 0x%02x > 8\n", in[data_buf_size-1] ); */ /* return gpg_error (GPG_ERR_BAD_KEY); */ /* } */ err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, data_buf, data_buf_size, NULL); xfree (data_buf); if (err) { log_error ("ecdh failed to create a plain text MPI: %s\n", gpg_strerror (err)); return err; } return err; } diff --git a/g10/keyid.c b/g10/keyid.c index 09940cbfe..08f684829 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -1,1480 +1,1487 @@ /* 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, 2023 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include "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 function 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" - EdDSA using the curve Ed25519. "cv25519" - ECDH using the curve X25519. "ky768_cv448 - Kyber-768 with X448 as second algo. "ky1025_bp512 - Kyber-1024 with BrainpoolP256r1 as second algo. "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". "unknown_N" - Unknown OpenPGP algorithm N. "E_1.3.6.1.4.1.11591.2.12242973" ECC with a bogus OID. Note that with Kyber we use "bp" as abbreviation for BrainpoolP and ignore the final r1 part. 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. Note that a more general version of this function exists as get_keyalgo_string. However, that has no special treatment for the old and unsupported Elgamal which we here print as xxxNNNN. */ char * pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) { const char *prefix = NULL; int dual = 0; char *curve; const char *name; 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; case PUBKEY_ALGO_KYBER: prefix = "ky"; dual = 1; break; case PUBKEY_ALGO_DIL3_25519: prefix = "dil3"; break; case PUBKEY_ALGO_DIL5_448: prefix = "dil5"; break; case PUBKEY_ALGO_SPHINX_SHA2: prefix = "sphinx_sha2"; break; } if (prefix && *prefix) { if (dual) { curve = openpgp_oid_to_str (pk->pkey[0]); /* Note that we prefer the abbreviated name of the curve. */ name = openpgp_oid_to_curve (curve, 2); if (!name) name = "unknown"; snprintf (buffer, bufsize, "%s%u_%s", prefix, nbits_from_pk (pk), name); xfree (curve); } else snprintf (buffer, bufsize, "%s%u", prefix, nbits_from_pk (pk)); } else if (prefix) { curve = openpgp_oid_to_str (pk->pkey[0]); 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; } /* Helper for compare_pubkey_string. This skips leading spaces, * commas and optional condition operators and returns a pointer to * the first non-space character or NULL in case of an error. The * length of a prefix consisting of letters is then returned ar PFXLEN * and the value of the number (e.g. 384 for "brainpoolP384r1") at * NUMBER. R_LENGTH receives the entire length of the algorithm name * which is terminated by a space, nul, or a comma. If R_CONDITION is * not NULL, 0 is stored for a leading "=", 1 for a ">", 2 for a ">=", * -1 for a "<", and -2 for a "<=". If R_CONDITION is NULL no * condition prefix is allowed. */ static const char * parse_one_algo_string (const char *str, size_t *pfxlen, unsigned int *number, size_t *r_length, int *r_condition) { int condition = 0; const char *result; while (spacep (str) || *str ==',') str++; if (!r_condition) ; else if (*str == '>' && str[1] == '=') condition = 2, str += 2; else if (*str == '>' ) condition = 1, str += 1; else if (*str == '<' && str[1] == '=') condition = -2, str += 2; else if (*str == '<') condition = -1, str += 1; else if (*str == '=') /* Default. */ str += 1; if (!alphap (str)) return NULL; /* Error. */ *pfxlen = 1; for (result = str++; alphap (str); str++) ++*pfxlen; while (*str == '-' || *str == '+') str++; *number = atoi (str); while (*str && !spacep (str) && *str != ',') str++; *r_length = str - result; if (r_condition) *r_condition = condition; return result; } /* Helper for compare_pubkey_string. If BPARSED is set to 0 on * return, an error in ASTR or BSTR was found and further checks are * not possible. */ static int compare_pubkey_string_part (const char *astr, const char *bstr_arg, size_t *bparsed) { const char *bstr = bstr_arg; size_t alen, apfxlen, blen, bpfxlen; unsigned int anumber, bnumber; int condition; *bparsed = 0; astr = parse_one_algo_string (astr, &apfxlen, &anumber, &alen, &condition); if (!astr) return 0; /* Invalid algorithm name. */ bstr = parse_one_algo_string (bstr, &bpfxlen, &bnumber, &blen, &condition); if (!bstr) return 0; /* Invalid algorithm name. */ *bparsed = blen + (bstr - bstr_arg); if (apfxlen != bpfxlen || ascii_strncasecmp (astr, bstr, apfxlen)) return 0; /* false. */ switch (condition) { case 2: return anumber >= bnumber; case 1: return anumber > bnumber; case -1: return anumber < bnumber; case -2: return anumber <= bnumber; } return alen == blen && !ascii_strncasecmp (astr, bstr, alen); } /* Check whether ASTR matches the constraints given by BSTR. ASTR may * be any algo string like "rsa2048", "ed25519" and BSTR may be a * constraint which is in the simplest case just another algo string. * BSTR may have more that one string in which case they are comma * separated and any match will return true. It is possible to prefix * BSTR with ">", ">=", "<=", or "<". That prefix operator is applied * to the number part of the algorithm, i.e. the first sequence of * digits found before end-of-string or a comma. Examples: * * | ASTR | BSTR | result | * |----------+----------------------+--------| * | rsa2048 | rsa2048 | true | * | rsa2048 | >=rsa2048 | true | * | rsa2048 | >rsa2048 | false | * | ed25519 | >rsa1024 | false | * | ed25519 | ed25519 | true | * | nistp384 | >nistp256 | true | * | nistp521 | >=rsa3072, >nistp384 | true | */ int compare_pubkey_string (const char *astr, const char *bstr) { size_t bparsed; int result; while (*bstr) { result = compare_pubkey_string_part (astr, bstr, &bparsed); if (result) return 1; if (!bparsed) return 0; /* Syntax error in ASTR or BSTR. */ bstr += bparsed; } return 0; } /* Hash a public key and allow to specify the to be used format. * Note that if the v5 format is requested for a v4 key, a 0x04 as * version is hashed instead of the 0x05. */ static void do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int use_v5) { unsigned int n; 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); n = use_v5? 10 : 6; /* 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 (pk->pubkey_algo == PUBKEY_ALGO_KYBER && i == 2) { /* Ugly: We need to re-construct the wire format of the * key parameter. It would be easier to use a second - * second index for pp and nn which could bump - * independet of i. */ + * index for pp and nn which we could bump independet of + * i. */ const char *p; p = gcry_mpi_get_opaque (pk->pkey[i], &nbits); - pp[i] = xmalloc ((nbits+7)/8 + 1); + nn[i] = (nbits+7)/8; + pp[i] = xmalloc (4 + nn[i] + 1); if (p) - memcpy (pp[i], p, (nbits+7)/8); + { + pp[i][0] = nn[i] >> 24; + pp[i][1] = nn[i] >> 16; + pp[i][2] = nn[i] >> 8; + pp[i][3] = nn[i]; + memcpy (pp[i] + 4 , p, nn[i]); + nn[i] += 4; + } else pp[i] = NULL; - nn[i] = (nbits+7)/8; n += nn[i]; } else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) { const char *p; int is_sos = 0; if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER2)) is_sos = 2; p = gcry_mpi_get_opaque (pk->pkey[i], &nbits); pp[i] = xmalloc ((nbits+7)/8 + is_sos); if (p) memcpy (pp[i] + is_sos, p, (nbits+7)/8); else pp[i] = NULL; if (is_sos) { if (*p) { nbits = ((nbits + 7) / 8) * 8; if (nbits >= 8 && !(*p & 0x80)) if (--nbits >= 7 && !(*p & 0x40)) if (--nbits >= 6 && !(*p & 0x20)) if (--nbits >= 5 && !(*p & 0x10)) if (--nbits >= 4 && !(*p & 0x08)) if (--nbits >= 3 && !(*p & 0x04)) if (--nbits >= 2 && !(*p & 0x02)) if (--nbits >= 1 && !(*p & 0x01)) --nbits; } pp[i][0] = (nbits >> 8); pp[i][1] = nbits; } nn[i] = (nbits+7)/8 + is_sos; 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]; } } } if (use_v5) { gcry_md_putc ( md, 0x9a ); /* ctb */ gcry_md_putc ( md, n >> 24 ); /* 4 byte length header (upper bits) */ gcry_md_putc ( md, n >> 16 ); } else { gcry_md_putc ( md, 0x99 ); /* ctb */ } gcry_md_putc ( md, n >> 8 ); /* lower bits of the 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 (use_v5) /* Hash the 32 bit length */ { n -= 10; gcry_md_putc ( md, n >> 24 ); gcry_md_putc ( md, n >> 16 ); gcry_md_putc ( md, n >> 8 ); gcry_md_putc ( md, n ); } 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]); } } } /* Hash a public key. This function is useful for v4 and v5 * fingerprints and for v3 or v4 key signing. */ void hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) { do_hash_public_key (md, pk, (pk->version == 5)); } /* 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) { if (! buffer) { len = KEYID_STR_SIZE; buffer = xtrymalloc (len); if (!buffer) return NULL; } 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(); } 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_FPR: { u32 keyid[2]; if (desc->fprlen == 32) { keyid[0] = buf32_to_u32 (desc->u.fpr); keyid[1] = buf32_to_u32 (desc->u.fpr+4); } else if (desc->fprlen == 20) { keyid[0] = buf32_to_u32 (desc->u.fpr+12); keyid[1] = buf32_to_u32 (desc->u.fpr+16); } else if (desc->fprlen == 16) return "?v3 fpr?"; else /* oops */ return "?vx fpr?"; return keystr(keyid); } default: BUG(); } } /* Compute the fingerprint and keyid and store it in PK. */ static void compute_fingerprint (PKT_public_key *pk) { const byte *dp; gcry_md_hd_t md; size_t len; if (gcry_md_open (&md, pk->version == 5 ? GCRY_MD_SHA256 : GCRY_MD_SHA1, 0)) BUG (); hash_public_key (md, pk); gcry_md_final (md); dp = gcry_md_read (md, 0); len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); log_assert (len <= MAX_FINGERPRINT_LEN); memcpy (pk->fpr, dp, len); pk->fprlen = len; if (pk->version == 5) { pk->keyid[0] = buf32_to_u32 (dp); pk->keyid[1] = buf32_to_u32 (dp+4); } else { pk->keyid[0] = buf32_to_u32 (dp+12); pk->keyid[1] = buf32_to_u32 (dp+16); } gcry_md_close( md); } /* * Get the keyid from the public key PK and store it at KEYID unless * this is NULL. Returns the 32 bit short keyid. */ u32 keyid_from_pk (PKT_public_key *pk, u32 *keyid) { u32 dummy_keyid[2]; if (!keyid) keyid = dummy_keyid; if (!pk->fprlen) compute_fingerprint (pk); keyid[0] = pk->keyid[0]; keyid[1] = pk->keyid[1]; if (pk->fprlen == 32) return keyid[0]; else return keyid[1]; } /* * Get the keyid from the fingerprint. This function is simple for * most keys, but has to do a key lookup for old v3 keys where the * keyid is not part of the fingerprint. */ 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 && fprint_len != 32) { /* 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_printhex (fprint, fprint_len, "Oops: keyid_from_fingerprint: no pubkey; fpr:"); keyid[0] = 0; keyid[1] = 0; } else keyid_from_pk (&pk, keyid); } else { const byte *dp = fprint; if (fprint_len == 20) /* v4 key */ { keyid[0] = buf32_to_u32 (dp+12); keyid[1] = buf32_to_u32 (dp+16); } else /* v5 key */ { keyid[0] = buf32_to_u32 (dp); keyid[1] = buf32_to_u32 (dp+4); } } 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]; /*FIXME:shortkeyid*/ } 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. For Kyber we return the * octet count of the Kyber part and not of the ECC (thus likely * values are 768 or 1024). */ unsigned int nbits_from_pk (PKT_public_key *pk) { if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) { unsigned int nbits; if (!gcry_mpi_get_opaque (pk->pkey[2], &nbits)) return 0; switch (nbits/8) { case 800: nbits = 512; break; case 1184: nbits = 768; break; case 1568: nbits = 1024; break; default: nbits = 0; break; /* Unkown version. */ } return nbits; } else 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 * dateonlystr_from_pk (PKT_public_key *pk) { static char buffer[MK_DATESTR_SIZE]; return mk_datestr (buffer, sizeof buffer, pk->timestamp); } /* Same as dateonlystr_from_pk but with a global option a full iso * timestamp is returned. In this case it shares a static buffer with * isotimestamp(). */ const char * datestr_from_pk (PKT_public_key *pk) { if (opt.flags.full_timestrings) return isotimestamp (pk->timestamp); else return dateonlystr_from_pk (pk); } const char * dateonlystr_from_sig (PKT_signature *sig ) { static char buffer[MK_DATESTR_SIZE]; return mk_datestr (buffer, sizeof buffer, sig->timestamp); } const char * datestr_from_sig (PKT_signature *sig ) { if (opt.flags.full_timestrings) return isotimestamp (sig->timestamp); else return dateonlystr_from_sig (sig); } const char * expirestr_from_pk (PKT_public_key *pk) { static char buffer[MK_DATESTR_SIZE]; if (!pk->expiredate) return _("never "); if (opt.flags.full_timestrings) return isotimestamp (pk->expiredate); 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 "); if (opt.flags.full_timestrings) return isotimestamp (sig->expiredate); 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 "); if (opt.flags.full_timestrings) return isotimestamp (pk->revoked.date); 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'; if ( (use & PUBKEY_USAGE_RENC) ) buffer[i++] = 'R'; if ( (use & PUBKEY_USAGE_TIME) ) buffer[i++] = 'T'; if ( (use & PUBKEY_USAGE_GROUP) ) buffer[i++] = 'G'; 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) { if (!pk->fprlen) compute_fingerprint (pk); if (!array) array = xmalloc (pk->fprlen); memcpy (array, pk->fpr, pk->fprlen); if (ret_len) *ret_len = pk->fprlen; return array; } /* * 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. This * version creates a v5 fingerprint even vor v4 keys. */ byte * v5_fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) { const byte *dp; gcry_md_hd_t md; if (pk->version == 5) return fingerprint_from_pk (pk, array, ret_len); if (gcry_md_open (&md, GCRY_MD_SHA256, 0)) BUG (); do_hash_public_key (md, pk, 1); gcry_md_final (md); dp = gcry_md_read (md, 0); if (!array) array = xmalloc (32); memcpy (array, dp, 32); gcry_md_close (md); if (ret_len) *ret_len = 32; return array; } /* * This is the core of fpr20_from_pk which directly takes a * fingerprint and its length instead of the public key. See below * for details. */ void fpr20_from_fpr (const byte *fpr, unsigned int fprlen, byte array[20]) { if (fprlen >= 32) /* v5 fingerprint (or larger) */ { memcpy (array + 0, fpr + 20, 4); memcpy (array + 4, fpr + 24, 4); memcpy (array + 8, fpr + 28, 4); memcpy (array + 12, fpr + 0, 4); /* kid[0] */ memcpy (array + 16, fpr + 4, 4); /* kid[1] */ } else if (fprlen == 20) /* v4 fingerprint */ memcpy (array, fpr, 20); else /* v3 or too short: fill up with zeroes. */ { memset (array, 0, 20); memcpy (array, fpr, fprlen); } } /* * Get FPR20 for the given PK/SK into ARRAY. * * FPR20 is special form of fingerprint of length 20 for the record of * trustdb. For v4key, having fingerprint with SHA-1, FPR20 is the * same one. For v5key, FPR20 is constructed from its fingerprint * with SHA-2, so that its kid of last 8-byte can be as same as * kid of v5key fingerprint. * */ void fpr20_from_pk (PKT_public_key *pk, byte array[20]) { if (!pk->fprlen) compute_fingerprint (pk); fpr20_from_fpr (pk->fpr, pk->fprlen, 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) { if (!pk->fprlen) compute_fingerprint (pk); if (!buffer) { buffer = xtrymalloc (2 * pk->fprlen + 1); if (!buffer) return NULL; } else if (buflen < 2 * pk->fprlen + 1) log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen); bin2hex (pk->fpr, pk->fprlen, buffer); return buffer; } /* Same as hexfingerprint but returns a v5 fingerprint also for a v4 * key. */ char * v5hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen) { char fprbuf[32]; if (pk->version == 5) return hexfingerprint (pk, buffer, buflen); if (!buffer) { buffer = xtrymalloc (2 * 32 + 1); if (!buffer) return NULL; } else if (buflen < 2 * 32 + 1) log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen); v5_fingerprint_from_pk (pk, fprbuf, NULL); return bin2hex (fprbuf, 32, 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 if (hexlen == 64 || hexlen == 50) /* v5 fingerprint */ { /* The v5 fingerprint is commonly printed truncated to 25 * octets. We accept the truncated as well as the full hex * version here and format it like this: * 19347 BC987 24640 25F99 DF3EC 2E000 0ED98 84892 E1F7B 3EA4C */ hexlen = 50; space = 10 * 5 + 9 + 1; } else /* Other fingerprint versions - print as is. */ { /* We truncated here so that we do not need to provide a buffer * of a length which is in reality never used. */ if (hexlen > MAX_FORMATTED_FINGERPRINT_LEN - 1) hexlen = MAX_FORMATTED_FINGERPRINT_LEN - 1; 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)) buffer[j ++] = ' '; if (i == 40 / 2) buffer[j ++] = ' '; buffer[j ++] = fingerprint[i]; } buffer[j ++] = 0; log_assert (j == space); } else if (hexlen == 50) /* v5 fingerprint */ { for (i=j=0; i < 50; i++) { if (i && !(i % 5)) buffer[j++] = ' '; buffer[j++] = fingerprint[i]; } buffer[j++] = 0; log_assert (j == space); } else { mem2str (buffer, fingerprint, space); } return buffer; } /* Return the so called KEYGRIP which is the SHA-1 hash of the public * key parameters expressed as an canonical encoded S-Exp. ARRAY must * be 20 bytes long. Returns 0 on success or an error code. If * GET_SECOND Is one and PK has dual algorithm, the keygrip of the * second algorithm is return; GPG_ERR_FALSE is returned if the algo * is not a dual algorithm. */ gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array, int get_second) { gpg_error_t err; gcry_sexp_t s_pkey; if (DBG_PACKET) log_debug ("get_keygrip for public key%s\n", get_second?" (second)":""); if (get_second && pk->pubkey_algo != PUBKEY_ALGO_KYBER) return gpg_error (GPG_ERR_FALSE); 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; case PUBKEY_ALGO_KYBER: if (get_second) { char tmpname[15]; snprintf (tmpname, sizeof tmpname, "kyber%u", nbits_from_pk (pk)); err = gcry_sexp_build (&s_pkey, NULL, "(public-key(%s(p%m)))", tmpname, pk->pkey[2]); } else { char *curve = openpgp_oid_to_str (pk->pkey[0]); if (!curve) err = gpg_error_from_syserror (); else { err = gcry_sexp_build (&s_pkey, NULL, 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)) { char *hexfpr; hexfpr = hexfingerprint (pk, NULL, 0); log_info ("error computing keygrip (fpr=%s)\n", hexfpr); xfree (hexfpr); memset (array, 0, 20); err = gpg_error (GPG_ERR_GENERAL); } else { if (DBG_PACKET) log_printhex (array, 20, "keygrip="); /* 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. For dual algorithms * the keygrips are delimited by a comma. */ gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) { gpg_error_t err; char *buf; unsigned char grip[KEYGRIP_LEN]; unsigned char grip2[KEYGRIP_LEN]; *r_grip = NULL; err = keygrip_from_pk (pk, grip, 0); if (!err) { if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) { err = keygrip_from_pk (pk, grip2, 1); if (err) goto leave; buf = xtrymalloc (2 * KEYGRIP_LEN * 2 + 1 + 1); } else buf = xtrymalloc (KEYGRIP_LEN * 2 + 1); if (!buf) { err = gpg_error_from_syserror (); goto leave; } bin2hex (grip, KEYGRIP_LEN, buf); if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) { buf[2*KEYGRIP_LEN] = ','; bin2hex (grip2, KEYGRIP_LEN, buf+2*KEYGRIP_LEN+1); } *r_grip = buf; } leave: return err; } /* Return a hexfied malloced string of the ECDH parameters for an ECDH * key from the public key PK. Returns NULL on error. */ char * ecdh_param_str_from_pk (PKT_public_key *pk) { const unsigned char *s; unsigned int n; if (!pk || pk->pubkey_algo != PUBKEY_ALGO_ECDH || !gcry_mpi_get_flag (pk->pkey[2], GCRYMPI_FLAG_OPAQUE) || !(s = gcry_mpi_get_opaque (pk->pkey[2], &n)) || !n) { gpg_err_set_errno (EINVAL); return NULL; /* Invalid parameter */ } n = (n+7)/8; return bin2hex (s, n, NULL); } diff --git a/g10/mainproc.c b/g10/mainproc.c index d2e00514f..6f3254e88 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1,2831 +1,2835 @@ /* mainproc.c - handle packets * Copyright (C) 1998-2009 Free Software Foundation, Inc. * Copyright (C) 2013-2014 Werner Koch * Copyright (C) 2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "packet.h" #include "../common/iobuf.h" #include "options.h" #include "keydb.h" #include "filter.h" #include "main.h" #include "../common/status.h" #include "../common/i18n.h" #include "trustdb.h" #include "keyserver-internal.h" #include "photoid.h" #include "../common/mbox-util.h" #include "call-dirmngr.h" #include "../common/compliance.h" /* Put an upper limit on nested packets. The 32 is an arbitrary value, a much lower should actually be sufficient. */ #define MAX_NESTING_DEPTH 32 /* An object to build a list of symkey packet info. */ struct symlist_item { struct symlist_item *next; int cipher_algo; int cfb_mode; int other_error; }; /* * Object to hold the processing context. */ typedef struct mainproc_context *CTX; struct mainproc_context { ctrl_t ctrl; struct mainproc_context *anchor; /* May be useful in the future. */ PKT_public_key *last_pubkey; PKT_user_id *last_user_id; md_filter_context_t mfx; int sigs_only; /* Process only signatures and reject all other stuff. */ int encrypt_only; /* Process only encryption messages. */ /* Name of the file with the complete signature or the file with the detached signature. This is currently only used to deduce the file name of the data file if that has not been given. */ const char *sigfilename; /* A structure to describe the signed data in case of a detached signature. */ struct { /* A file descriptor of the signed data. Only used if not -1. */ gnupg_fd_t data_fd; /* A list of filenames with the data files or NULL. This is only used if DATA_FD is -1. */ strlist_t data_names; /* Flag to indicated that either one of the next previous fields is used. This is only needed for better readability. */ int used; } signed_data; DEK *dek; int last_was_session_key; kbnode_t list; /* The current list of packets. */ iobuf_t iobuf; /* Used to get the filename etc. */ int trustletter; /* Temporary usage in list_node. */ ulong symkeys; /* Number of symmetrically encrypted session keys. */ struct pubkey_enc_list *pkenc_list; /* List of encryption packets. */ struct symlist_item *symenc_list; /* List of sym. encryption packets. */ int seen_pkt_encrypted_aead; /* PKT_ENCRYPTED_AEAD packet seen. */ int seen_pkt_encrypted_mdc; /* PKT_ENCRYPTED_MDC packet seen. */ struct { unsigned int sig_seen:1; /* Set to true if a signature packet has been seen. */ unsigned int data:1; /* Any data packet seen */ unsigned int uncompress_failed:1; } any; }; /* Counter with the number of literal data packets seen. Note that * this is also bumped at the end of an encryption. This counter is * used for a basic consistency check of a received PGP message. */ static int literals_seen; /*** Local prototypes. ***/ static int do_proc_packets (CTX c, iobuf_t a); static void list_node (CTX c, kbnode_t node); static void proc_tree (CTX c, kbnode_t node); /*** Functions. ***/ /* Reset the literal data counter. This is required to setup a new * decryption or verification context. */ void reset_literals_seen(void) { literals_seen = 0; } static void release_list( CTX c ) { proc_tree (c, c->list); release_kbnode (c->list); while (c->pkenc_list) { struct pubkey_enc_list *tmp = c->pkenc_list->next; mpi_release (c->pkenc_list->data[0]); mpi_release (c->pkenc_list->data[1]); + mpi_release (c->pkenc_list->data[2]); + mpi_release (c->pkenc_list->data[3]); xfree (c->pkenc_list); c->pkenc_list = tmp; } c->pkenc_list = NULL; while (c->symenc_list) { struct symlist_item *tmp = c->symenc_list->next; xfree (c->symenc_list); c->symenc_list = tmp; } c->symenc_list = NULL; c->list = NULL; c->any.data = 0; c->any.uncompress_failed = 0; c->last_was_session_key = 0; c->seen_pkt_encrypted_aead = 0; c->seen_pkt_encrypted_mdc = 0; xfree (c->dek); c->dek = NULL; } static int add_onepass_sig (CTX c, PACKET *pkt) { kbnode_t node; if (c->list) /* Add another packet. */ add_kbnode (c->list, new_kbnode (pkt)); else /* Insert the first one. */ c->list = node = new_kbnode (pkt); return 1; } static int add_gpg_control (CTX c, PACKET *pkt) { if ( pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) { /* New clear text signature. * Process the last one and reset everything */ release_list(c); } if (c->list) /* Add another packet. */ add_kbnode (c->list, new_kbnode (pkt)); else /* Insert the first one. */ c->list = new_kbnode (pkt); return 1; } static int add_user_id (CTX c, PACKET *pkt) { if (!c->list) { log_error ("orphaned user ID\n"); return 0; } add_kbnode (c->list, new_kbnode (pkt)); return 1; } static int add_subkey (CTX c, PACKET *pkt) { if (!c->list) { log_error ("subkey w/o mainkey\n"); return 0; } add_kbnode (c->list, new_kbnode (pkt)); return 1; } static int add_ring_trust (CTX c, PACKET *pkt) { if (!c->list) { log_error ("ring trust w/o key\n"); return 0; } add_kbnode (c->list, new_kbnode (pkt)); return 1; } static int add_signature (CTX c, PACKET *pkt) { kbnode_t node; c->any.sig_seen = 1; if (pkt->pkttype == PKT_SIGNATURE && !c->list) { /* This is the first signature for the following datafile. * GPG does not write such packets; instead it always uses * onepass-sig packets. The drawback of PGP's method * of prepending the signature to the data is * that it is not possible to make a signature from data read * from stdin. (GPG is able to read PGP stuff anyway.) */ node = new_kbnode (pkt); c->list = node; return 1; } else if (!c->list) return 0; /* oops (invalid packet sequence)*/ else if (!c->list->pkt) BUG(); /* so nicht */ /* Add a new signature node item at the end. */ node = new_kbnode (pkt); add_kbnode (c->list, node); return 1; } static gpg_error_t symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen) { gpg_error_t err; gcry_cipher_hd_t hd; unsigned int noncelen, keylen; enum gcry_cipher_modes ciphermode; if (dek->use_aead) { err = openpgp_aead_algo_info (dek->use_aead, &ciphermode, &noncelen); if (err) return err; } else { ciphermode = GCRY_CIPHER_MODE_CFB; noncelen = 0; } /* Check that the session key has a size of 16 to 32 bytes. */ if ((dek->use_aead && (slen < (noncelen + 16 + 16) || slen > (noncelen + 32 + 16))) || (!dek->use_aead && (slen < 17 || slen > 33))) { log_error ( _("weird size for an encrypted session key (%d)\n"), (int)slen); return gpg_error (GPG_ERR_BAD_KEY); } err = openpgp_cipher_open (&hd, dek->algo, ciphermode, GCRY_CIPHER_SECURE); if (!err) err = gcry_cipher_setkey (hd, dek->key, dek->keylen); if (!err) err = gcry_cipher_setiv (hd, noncelen? seskey : NULL, noncelen); if (err) goto leave; if (dek->use_aead) { byte ad[4]; ad[0] = (0xc0 | PKT_SYMKEY_ENC); ad[1] = 5; ad[2] = dek->algo; ad[3] = dek->use_aead; err = gcry_cipher_authenticate (hd, ad, 4); if (err) goto leave; gcry_cipher_final (hd); keylen = slen - noncelen - 16; err = gcry_cipher_decrypt (hd, seskey+noncelen, keylen, NULL, 0); if (err) goto leave; err = gcry_cipher_checktag (hd, seskey+noncelen+keylen, 16); if (err) goto leave; /* Now we replace the dek components with the real session key to * decrypt the contents of the sequencing packet. */ if (keylen > DIM(dek->key)) { err = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } dek->keylen = keylen; memcpy (dek->key, seskey + noncelen, dek->keylen); } else { gcry_cipher_decrypt (hd, seskey, slen, NULL, 0 ); /* Here we can only test whether the algo given in decrypted * session key is a valid OpenPGP algo. With 11 defined * symmetric algorithms we will miss 4.3% of wrong passphrases * here. The actual checking is done later during bulk * decryption; we can't bring this check forward easily. We * need to use the GPG_ERR_CHECKSUM so that we won't run into * the gnupg < 2.2 bug compatible case which would terminate the * process on GPG_ERR_CIPHER_ALGO. Note that with AEAD (above) * we will have a reliable test here. */ if (openpgp_cipher_test_algo (seskey[0]) || openpgp_cipher_get_algo_keylen (seskey[0]) != slen - 1) { err = gpg_error (GPG_ERR_CHECKSUM); goto leave; } /* Now we replace the dek components with the real session key to * decrypt the contents of the sequencing packet. */ keylen = slen-1; if (keylen > DIM(dek->key)) { err = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } dek->algo = seskey[0]; dek->keylen = keylen; memcpy (dek->key, seskey + 1, dek->keylen); } /*log_hexdump( "thekey", dek->key, dek->keylen );*/ leave: gcry_cipher_close (hd); return err; } static void proc_symkey_enc (CTX c, PACKET *pkt) { gpg_error_t err; PKT_symkey_enc *enc; enc = pkt->pkt.symkey_enc; if (!enc) log_error ("invalid symkey encrypted packet\n"); else if(!c->dek) { int algo = enc->cipher_algo; const char *s = openpgp_cipher_algo_name (algo); const char *a = (enc->aead_algo ? openpgp_aead_algo_name (enc->aead_algo) /**/ : "CFB"); if (!openpgp_cipher_test_algo (algo)) { if (!opt.quiet) { if (enc->seskeylen) log_info (_("%s.%s encrypted session key\n"), s, a ); else log_info (_("%s.%s encrypted data\n"), s, a ); } } else { log_error (_("encrypted with unknown algorithm %d.%s\n"), algo, a); s = NULL; /* Force a goto leave. */ } if (openpgp_md_test_algo (enc->s2k.hash_algo)) { log_error(_("passphrase generated with unknown digest" " algorithm %d\n"),enc->s2k.hash_algo); s = NULL; } c->last_was_session_key = 2; if (!s || opt.list_only) goto leave; if (opt.override_session_key) { c->dek = xmalloc_clear (sizeof *c->dek); if (get_override_session_key (c->dek, opt.override_session_key)) { xfree (c->dek); c->dek = NULL; } } else { c->dek = passphrase_to_dek (algo, &enc->s2k, 0, 0, NULL, GETPASSWORD_FLAG_SYMDECRYPT, NULL); if (c->dek) { c->dek->symmetric = 1; c->dek->use_aead = enc->aead_algo; /* FIXME: This doesn't work perfectly if a symmetric key comes before a public key in the message - if the user doesn't know the passphrase, then there is a chance that the "decrypted" algorithm will happen to be a valid one, which will make the returned dek appear valid, so we won't try any public keys that come later. */ if (enc->seskeylen) { err = symkey_decrypt_seskey (c->dek, enc->seskey, enc->seskeylen); if (err) { log_info ("decryption of the symmetrically encrypted" " session key failed: %s\n", gpg_strerror (err)); if (gpg_err_code (err) != GPG_ERR_BAD_KEY && gpg_err_code (err) != GPG_ERR_CHECKSUM) log_fatal ("process terminated to be bug compatible" " with GnuPG <= 2.2\n"); else write_status_text (STATUS_ERROR, "symkey_decrypt.maybe_error" " 11_BAD_PASSPHRASE"); if (c->dek->s2k_cacheid[0]) { if (opt.debug) log_debug ("cleared passphrase cached with ID:" " %s\n", c->dek->s2k_cacheid); passphrase_clear_cache (c->dek->s2k_cacheid); } xfree (c->dek); c->dek = NULL; } } else c->dek->algo_info_printed = 1; } } } leave: /* Record infos from the packet. */ { struct symlist_item *symitem; symitem = xcalloc (1, sizeof *symitem); if (enc) { symitem->cipher_algo = enc->cipher_algo; symitem->cfb_mode = !enc->aead_algo; } else symitem->other_error = 1; symitem->next = c->symenc_list; c->symenc_list = symitem; } c->symkeys++; free_packet (pkt, NULL); } static void proc_pubkey_enc (CTX c, PACKET *pkt) { PKT_pubkey_enc *enc; /* Check whether the secret key is available and store in this case. */ c->last_was_session_key = 1; enc = pkt->pkt.pubkey_enc; /*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/ /* Hmmm: why do I have this algo check here - anyway there is * function to check it. */ if (opt.verbose) log_info (_("public key is %s\n"), keystr (enc->keyid)); if (is_status_enabled ()) { char buf[50]; snprintf (buf, sizeof buf, "%08lX%08lX %d 0", (ulong)enc->keyid[0], (ulong)enc->keyid[1], enc->pubkey_algo); write_status_text (STATUS_ENC_TO, buf); } if (!opt.list_only && !opt.override_session_key) { struct pubkey_enc_list *x = xmalloc (sizeof *x); x->keyid[0] = enc->keyid[0]; x->keyid[1] = enc->keyid[1]; x->pubkey_algo = enc->pubkey_algo; x->result = -1; - x->data[0] = x->data[1] = NULL; + x->data[0] = x->data[1] = x->data[2] = x->data[3] = NULL; if (enc->data[0]) { x->data[0] = mpi_copy (enc->data[0]); x->data[1] = mpi_copy (enc->data[1]); + x->data[2] = mpi_copy (enc->data[2]); + x->data[3] = mpi_copy (enc->data[3]); } x->next = c->pkenc_list; c->pkenc_list = x; } free_packet(pkt, NULL); } /* * Print the list of public key encrypted packets which we could * not decrypt. */ static void print_pkenc_list (ctrl_t ctrl, struct pubkey_enc_list *list) { for (; list; list = list->next) { PKT_public_key *pk; char pkstrbuf[PUBKEY_STRING_SIZE]; char *p; pk = xmalloc_clear (sizeof *pk); pk->pubkey_algo = list->pubkey_algo; if (!get_pubkey (ctrl, pk, list->keyid)) { pubkey_string (pk, pkstrbuf, sizeof pkstrbuf); log_info (_("encrypted with %s key, ID %s, created %s\n"), pkstrbuf, keystr_from_pk (pk), strtimestamp (pk->timestamp)); p = get_user_id_native (ctrl, list->keyid); log_printf (_(" \"%s\"\n"), p); xfree (p); } else log_info (_("encrypted with %s key, ID %s\n"), openpgp_pk_algo_name (list->pubkey_algo), keystr(list->keyid)); free_public_key (pk); } } static void proc_encrypted (CTX c, PACKET *pkt) { int result = 0; int early_plaintext = literals_seen; unsigned int compliance_de_vs = 0; if (pkt->pkttype == PKT_ENCRYPTED_AEAD) c->seen_pkt_encrypted_aead = 1; if (pkt->pkttype == PKT_ENCRYPTED_MDC) c->seen_pkt_encrypted_mdc = 1; if (early_plaintext) { log_info (_("WARNING: multiple plaintexts seen\n")); write_status_errcode ("decryption.early_plaintext", GPG_ERR_BAD_DATA); /* We fail only later so that we can print some more info first. */ } if (!opt.quiet) { if (c->symkeys>1) log_info (_("encrypted with %lu passphrases\n"), c->symkeys); else if (c->symkeys == 1) log_info (_("encrypted with 1 passphrase\n")); print_pkenc_list (c->ctrl, c->pkenc_list); } /* Figure out the session key by looking at all pkenc packets. */ if (opt.list_only || c->dek) ; else if (opt.override_session_key) { c->dek = xmalloc_clear (sizeof *c->dek); result = get_override_session_key (c->dek, opt.override_session_key); if (result) { xfree (c->dek); c->dek = NULL; log_info (_("public key decryption failed: %s\n"), gpg_strerror (result)); write_status_error ("pkdecrypt_failed", result); } } else if (c->pkenc_list) { c->dek = xmalloc_secure_clear (sizeof *c->dek); result = get_session_key (c->ctrl, c->pkenc_list, c->dek); if (is_status_enabled ()) { struct pubkey_enc_list *list; for (list = c->pkenc_list; list; list = list->next) if (list->result) { /* Key was not tried or it caused an error. */ char buf[20]; snprintf (buf, sizeof buf, "%08lX%08lX", (ulong)list->keyid[0], (ulong)list->keyid[1]); write_status_text (STATUS_NO_SECKEY, buf); } } if (result) { log_info (_("public key decryption failed: %s\n"), gpg_strerror (result)); write_status_error ("pkdecrypt_failed", result); /* Error: Delete the DEK. */ xfree (c->dek); c->dek = NULL; } } if (c->dek && opt.verbose > 1) log_info (_("public key encrypted data: good DEK\n")); write_status (STATUS_BEGIN_DECRYPTION); /*log_debug("dat: %sencrypted data\n", c->dek?"":"conventional ");*/ if (opt.list_only) result = -1; else if (!c->dek && !c->last_was_session_key) { int algo; STRING2KEY s2kbuf; STRING2KEY *s2k = NULL; int canceled; if (opt.override_session_key) { c->dek = xmalloc_clear (sizeof *c->dek); result = get_override_session_key (c->dek, opt.override_session_key); if (result) { xfree (c->dek); c->dek = NULL; } } else { /* Assume this is old style conventional encrypted data. */ algo = opt.def_cipher_algo; if (algo) log_info (_("assuming %s encrypted data\n"), openpgp_cipher_algo_name (algo)); else if (openpgp_cipher_test_algo (CIPHER_ALGO_IDEA)) { algo = opt.def_cipher_algo; if (!algo) algo = opt.s2k_cipher_algo; log_info (_("IDEA cipher unavailable, " "optimistically attempting to use %s instead\n"), openpgp_cipher_algo_name (algo)); } else { algo = CIPHER_ALGO_IDEA; if (!opt.s2k_digest_algo) { /* If no digest is given we assume SHA-1. */ s2kbuf.mode = 0; s2kbuf.hash_algo = DIGEST_ALGO_SHA1; s2k = &s2kbuf; } log_info (_("assuming %s encrypted data\n"), "IDEA"); } c->dek = passphrase_to_dek (algo, s2k, 0, 0, NULL, GETPASSWORD_FLAG_SYMDECRYPT, &canceled); if (c->dek) c->dek->algo_info_printed = 1; else if (canceled) result = gpg_error (GPG_ERR_CANCELED); else result = gpg_error (GPG_ERR_INV_PASSPHRASE); } } else if (!c->dek) { if (c->symkeys && !c->pkenc_list) result = gpg_error (GPG_ERR_BAD_KEY); if (!result) result = gpg_error (GPG_ERR_NO_SECKEY); } /* Compute compliance with CO_DE_VS. */ if (!result && (is_status_enabled () || opt.flags.require_compliance) /* Overriding session key voids compliance. */ && !opt.override_session_key /* Check symmetric cipher. */ && gnupg_gcrypt_is_compliant (CO_DE_VS) && gnupg_cipher_is_compliant (CO_DE_VS, c->dek->algo, GCRY_CIPHER_MODE_CFB)) { struct pubkey_enc_list *i; struct symlist_item *si; int compliant = 1; PKT_public_key *pk = xmalloc (sizeof *pk); if ( !(c->pkenc_list || c->symkeys) ) log_debug ("%s: where else did the session key come from?\n", __func__); /* Check that all seen symmetric key packets use compliant * algos. This is so that no non-compliant encrypted session * key can be sneaked in. */ for (si = c->symenc_list; si && compliant; si = si->next) { if (!si->cfb_mode || !gnupg_cipher_is_compliant (CO_DE_VS, si->cipher_algo, GCRY_CIPHER_MODE_CFB)) compliant = 0; } /* Check that every known public key used to encrypt the session key * is compliant. */ for (i = c->pkenc_list; i && compliant; i = i->next) { memset (pk, 0, sizeof *pk); pk->pubkey_algo = i->pubkey_algo; if (!get_pubkey (c->ctrl, pk, i->keyid) && !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey, nbits_from_pk (pk), NULL)) compliant = 0; release_public_key_parts (pk); } xfree (pk); if (compliant) compliance_de_vs |= 1; } if (!result) { int compl_error; result = decrypt_data (c->ctrl, c, pkt->pkt.encrypted, c->dek, &compl_error); if (!result && !compl_error) compliance_de_vs |= 2; } /* Trigger the deferred error. The second condition makes sure that a * log_error printed in the cry_cipher_checktag never gets ignored. */ if (!result && early_plaintext) result = gpg_error (GPG_ERR_BAD_DATA); else if (!result && pkt->pkt.encrypted->aead_algo && log_get_errorcount (0)) result = gpg_error (GPG_ERR_BAD_SIGNATURE); if (result == -1) ; else if (!result && !opt.ignore_mdc_error && !pkt->pkt.encrypted->mdc_method && !pkt->pkt.encrypted->aead_algo) { /* The message has been decrypted but does not carry an MDC or * uses AEAD encryption. --ignore-mdc-error has also not been * used. To avoid attacks changing an MDC message to a non-MDC * message, we fail here. */ log_error (_("WARNING: message was not integrity protected\n")); if (!pkt->pkt.encrypted->mdc_method && (openpgp_cipher_get_algo_blklen (c->dek->algo) == 8 || c->dek->algo == CIPHER_ALGO_TWOFISH)) { /* Before 2.2.8 we did not fail hard for a missing MDC if * one of the old ciphers where used. Although these cases * are rare in practice we print a hint on how to decrypt * such messages. */ log_string (GPGRT_LOGLVL_INFO, _("Hint: If this message was created before the year 2003 it is\n" "likely that this message is legitimate. This is because back\n" "then integrity protection was not widely used.\n")); log_info (_("Use the option '%s' to decrypt anyway.\n"), "--ignore-mdc-error"); write_status_errcode ("nomdc_with_legacy_cipher", GPG_ERR_DECRYPT_FAILED); } log_info (_("decryption forced to fail!\n")); write_status (STATUS_DECRYPTION_FAILED); } else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE && !pkt->pkt.encrypted->aead_algo && opt.ignore_mdc_error)) { /* All is fine or for an MDC message the MDC failed but the * --ignore-mdc-error option is active. For compatibility * reasons we issue GOODMDC also for AEAD messages. */ write_status (STATUS_DECRYPTION_OKAY); if (opt.verbose > 1) log_info(_("decryption okay\n")); if (pkt->pkt.encrypted->aead_algo) { write_status (STATUS_GOODMDC); compliance_de_vs |= 4; } else if (pkt->pkt.encrypted->mdc_method && !result) { write_status (STATUS_GOODMDC); compliance_de_vs |= 4; } else log_info (_("WARNING: message was not integrity protected\n")); } else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE || gpg_err_code (result) == GPG_ERR_TRUNCATED) { glo_ctrl.lasterr = result; log_error (_("WARNING: encrypted message has been manipulated!\n")); write_status (STATUS_BADMDC); write_status (STATUS_DECRYPTION_FAILED); } else { if (gpg_err_code (result) == GPG_ERR_BAD_KEY || gpg_err_code (result) == GPG_ERR_CHECKSUM || gpg_err_code (result) == GPG_ERR_CIPHER_ALGO) { if (c->symkeys) write_status_text (STATUS_ERROR, "symkey_decrypt.maybe_error" " 11_BAD_PASSPHRASE"); if (c->dek && *c->dek->s2k_cacheid != '\0') { if (opt.debug) log_debug ("cleared passphrase cached with ID: %s\n", c->dek->s2k_cacheid); passphrase_clear_cache (c->dek->s2k_cacheid); } } glo_ctrl.lasterr = result; write_status (STATUS_DECRYPTION_FAILED); log_error (_("decryption failed: %s\n"), gpg_strerror (result)); /* Hmmm: does this work when we have encrypted using multiple * ways to specify the session key (symmmetric and PK). */ } /* If we concluded that the decryption was compliant, issue a * compliance status before the end of the decryption status. */ if (compliance_de_vs == (4|2|1)) { write_status_strings (STATUS_DECRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS), NULL); } xfree (c->dek); c->dek = NULL; free_packet (pkt, NULL); c->last_was_session_key = 0; write_status (STATUS_END_DECRYPTION); /* Bump the counter even if we have not seen a literal data packet * inside an encryption container. This acts as a sentinel in case * a misplace extra literal data packets follows after this * encrypted packet. */ literals_seen++; /* The --require-compliance option allows one to simplify decryption in * de-vs compliance mode by just looking at the exit status. */ if (opt.flags.require_compliance && opt.compliance == CO_DE_VS && compliance_de_vs != (4|2|1)) { log_error (_("operation forced to fail due to" " unfulfilled compliance rules\n")); g10_errors_seen = 1; } } static int have_seen_pkt_encrypted_aead_or_mdc( CTX c ) { CTX cc; for (cc = c; cc; cc = cc->anchor) { if (cc->seen_pkt_encrypted_aead) return 1; if (cc->seen_pkt_encrypted_mdc) return 1; } return 0; } static void proc_plaintext( CTX c, PACKET *pkt ) { PKT_plaintext *pt = pkt->pkt.plaintext; int any, clearsig, rc; kbnode_t n; unsigned char *extrahash; size_t extrahashlen; /* This is a literal data packet. Bump a counter for later checks. */ literals_seen++; if (pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8)) log_info (_("Note: sender requested \"for-your-eyes-only\"\n")); else if (opt.verbose) { /* We don't use print_utf8_buffer because that would require a * string change which we don't want in 2.2. It is also not * clear whether the filename is always utf-8 encoded. */ char *tmp = make_printable_string (pt->name, pt->namelen, 0); log_info (_("original file name='%.*s'\n"), (int)strlen (tmp), tmp); xfree (tmp); } free_md_filter_context (&c->mfx); if (gcry_md_open (&c->mfx.md, 0, 0)) BUG (); /* fixme: we may need to push the textfilter if we have sigclass 1 * and no armoring - Not yet tested * Hmmm, why don't we need it at all if we have sigclass 1 * Should we assume that plaintext in mode 't' has always sigclass 1?? * See: Russ Allbery's mail 1999-02-09 */ any = clearsig = 0; for (n=c->list; n; n = n->next ) { if (n->pkt->pkttype == PKT_ONEPASS_SIG) { /* The onepass signature case. */ if (n->pkt->pkt.onepass_sig->digest_algo) { if (!opt.skip_verify) gcry_md_enable (c->mfx.md, n->pkt->pkt.onepass_sig->digest_algo); any = 1; } } else if (n->pkt->pkttype == PKT_GPG_CONTROL && n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START) { /* The clearsigned message case. */ size_t datalen = n->pkt->pkt.gpg_control->datalen; const byte *data = n->pkt->pkt.gpg_control->data; /* Check that we have at least the sigclass and one hash. */ if (datalen < 2) log_fatal ("invalid control packet CTRLPKT_CLEARSIGN_START\n"); /* Note that we don't set the clearsig flag for not-dash-escaped * documents. */ clearsig = (*data == 0x01); for (data++, datalen--; datalen; datalen--, data++) if (!opt.skip_verify) gcry_md_enable (c->mfx.md, *data); any = 1; break; /* Stop here as one-pass signature packets are not expected. */ } else if (n->pkt->pkttype == PKT_SIGNATURE) { /* The SIG+LITERAL case that PGP used to use. */ if (!opt.skip_verify) gcry_md_enable (c->mfx.md, n->pkt->pkt.signature->digest_algo); any = 1; } } if (!any && !opt.skip_verify && !have_seen_pkt_encrypted_aead_or_mdc(c)) { /* This is for the old GPG LITERAL+SIG case. It's not legal according to 2440, so hopefully it won't come up that often. There is no good way to specify what algorithms to use in that case, so these there are the historical answer. */ gcry_md_enable (c->mfx.md, DIGEST_ALGO_RMD160); gcry_md_enable (c->mfx.md, DIGEST_ALGO_SHA1); } if (DBG_HASHING) { gcry_md_debug (c->mfx.md, "verify"); if (c->mfx.md2) gcry_md_debug (c->mfx.md2, "verify2"); } rc=0; if (literals_seen > 1) { log_info (_("WARNING: multiple plaintexts seen\n")); write_status_text (STATUS_ERROR, "proc_pkt.plaintext 89_BAD_DATA"); log_inc_errorcount (); rc = gpg_error (GPG_ERR_UNEXPECTED); } if (!rc) { /* It we are in --verify mode, we do not want to output the * signed text. However, if --output is also used we do what * has been requested and write out the signed data. */ rc = handle_plaintext (pt, &c->mfx, (opt.outfp || opt.outfile)? 0 : c->sigs_only, clearsig); if (gpg_err_code (rc) == GPG_ERR_EACCES && !c->sigs_only) { /* Can't write output but we hash it anyway to check the signature. */ rc = handle_plaintext( pt, &c->mfx, 1, clearsig ); } } if (rc) log_error ("handle plaintext failed: %s\n", gpg_strerror (rc)); /* We add a marker control packet instead of the plaintext packet. * This is so that we can later detect invalid packet sequences. * The packet is further used to convey extra data from the * plaintext packet to the signature verification. */ extrahash = xtrymalloc (6 + pt->namelen); if (!extrahash) { /* No way to return an error. */ rc = gpg_error_from_syserror (); log_error ("malloc failed in %s: %s\n", __func__, gpg_strerror (rc)); extrahashlen = 0; } else { extrahash[0] = pt->mode; extrahash[1] = pt->namelen; if (pt->namelen) memcpy (extrahash+2, pt->name, pt->namelen); extrahashlen = 2 + pt->namelen; extrahash[extrahashlen++] = pt->timestamp >> 24; extrahash[extrahashlen++] = pt->timestamp >> 16; extrahash[extrahashlen++] = pt->timestamp >> 8; extrahash[extrahashlen++] = pt->timestamp ; } free_packet (pkt, NULL); c->last_was_session_key = 0; n = new_kbnode (create_gpg_control (CTRLPKT_PLAINTEXT_MARK, extrahash, extrahashlen)); xfree (extrahash); if (c->list) add_kbnode (c->list, n); else c->list = n; } static int proc_compressed_cb (iobuf_t a, void *info) { if ( ((CTX)info)->signed_data.used && ((CTX)info)->signed_data.data_fd != GNUPG_INVALID_FD) return proc_signature_packets_by_fd (((CTX)info)->ctrl, info, a, ((CTX)info)->signed_data.data_fd); else return proc_signature_packets (((CTX)info)->ctrl, info, a, ((CTX)info)->signed_data.data_names, ((CTX)info)->sigfilename ); } static int proc_encrypt_cb (iobuf_t a, void *info ) { CTX c = info; return proc_encryption_packets (c->ctrl, info, a ); } static int proc_compressed (CTX c, PACKET *pkt) { PKT_compressed *zd = pkt->pkt.compressed; int rc; /*printf("zip: compressed data packet\n");*/ if (c->sigs_only) rc = handle_compressed (c->ctrl, c, zd, proc_compressed_cb, c); else if( c->encrypt_only ) rc = handle_compressed (c->ctrl, c, zd, proc_encrypt_cb, c); else rc = handle_compressed (c->ctrl, c, zd, NULL, NULL); if (gpg_err_code (rc) == GPG_ERR_BAD_DATA) { if (!c->any.uncompress_failed) { CTX cc; for (cc=c; cc; cc = cc->anchor) cc->any.uncompress_failed = 1; log_error ("uncompressing failed: %s\n", gpg_strerror (rc)); } } else if (rc) log_error ("uncompressing failed: %s\n", gpg_strerror (rc)); free_packet (pkt, NULL); c->last_was_session_key = 0; return rc; } /* * Check the signature. If R_PK is not NULL a copy of the public key * used to verify the signature will be stored there, or NULL if not * found. If FORCED_PK is not NULL, this public key is used to verify * _data signatures_ and no key lookup is done. Returns: 0 = valid * signature or an error code */ static int do_check_sig (CTX c, kbnode_t node, const void *extrahash, size_t extrahashlen, PKT_public_key *forced_pk, int *is_selfsig, int *is_expkey, int *is_revkey, PKT_public_key **r_pk) { PKT_signature *sig; gcry_md_hd_t md = NULL; gcry_md_hd_t md2 = NULL; gcry_md_hd_t md_good = NULL; int algo, rc; if (r_pk) *r_pk = NULL; log_assert (node->pkt->pkttype == PKT_SIGNATURE); if (is_selfsig) *is_selfsig = 0; sig = node->pkt->pkt.signature; algo = sig->digest_algo; rc = openpgp_md_test_algo (algo); if (rc) return rc; if (sig->sig_class == 0x00) { if (c->mfx.md) { if (gcry_md_copy (&md, c->mfx.md )) BUG (); } else /* detached signature */ { /* check_signature() will enable the md. */ if (gcry_md_open (&md, 0, 0 )) BUG (); } } else if (sig->sig_class == 0x01) { /* How do we know that we have to hash the (already hashed) text in canonical mode ??? (calculating both modes???) */ if (c->mfx.md) { if (gcry_md_copy (&md, c->mfx.md )) BUG (); if (c->mfx.md2 && gcry_md_copy (&md2, c->mfx.md2)) BUG (); } else /* detached signature */ { log_debug ("Do we really need this here?"); /* check_signature() will enable the md*/ if (gcry_md_open (&md, 0, 0 )) BUG (); if (gcry_md_open (&md2, 0, 0 )) BUG (); } } else if ((sig->sig_class&~3) == 0x10 || sig->sig_class == 0x18 || sig->sig_class == 0x1f || sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) { if (c->list->pkt->pkttype == PKT_PUBLIC_KEY || c->list->pkt->pkttype == PKT_PUBLIC_SUBKEY) { return check_key_signature (c->ctrl, c->list, node, is_selfsig); } else if (sig->sig_class == 0x20) { log_error (_("standalone revocation - " "use \"gpg --import\" to apply\n")); return GPG_ERR_NOT_PROCESSED; } else { log_error ("invalid root packet for sigclass %02x\n", sig->sig_class); return GPG_ERR_SIG_CLASS; } } else return GPG_ERR_SIG_CLASS; /* We only get here if we are checking the signature of a binary (0x00) or text document (0x01). */ rc = check_signature2 (c->ctrl, sig, md, extrahash, extrahashlen, forced_pk, NULL, is_expkey, is_revkey, r_pk); if (! rc) md_good = md; else if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2) { PKT_public_key *pk2; rc = check_signature2 (c->ctrl, sig, md2, extrahash, extrahashlen, forced_pk, NULL, is_expkey, is_revkey, r_pk? &pk2 : NULL); if (!rc) { md_good = md2; if (r_pk) { free_public_key (*r_pk); *r_pk = pk2; } } } if (md_good) { unsigned char *buffer = gcry_md_read (md_good, sig->digest_algo); sig->digest_len = gcry_md_get_algo_dlen (map_md_openpgp_to_gcry (algo)); memcpy (sig->digest, buffer, sig->digest_len); } gcry_md_close (md); gcry_md_close (md2); return rc; } static void print_userid (PACKET *pkt) { if (!pkt) BUG(); if (pkt->pkttype != PKT_USER_ID) { es_printf ("ERROR: unexpected packet type %d", pkt->pkttype ); return; } if (opt.with_colons) { if (pkt->pkt.user_id->attrib_data) es_printf("%u %lu", pkt->pkt.user_id->numattribs, pkt->pkt.user_id->attrib_len); else es_write_sanitized (es_stdout, pkt->pkt.user_id->name, pkt->pkt.user_id->len, ":", NULL); } else print_utf8_buffer (es_stdout, pkt->pkt.user_id->name, pkt->pkt.user_id->len ); } /* * List the keyblock in a user friendly way */ static void list_node (CTX c, kbnode_t node) { if (!node) ; else if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk = node->pkt->pkt.public_key; if (opt.with_colons) { u32 keyid[2]; keyid_from_pk( pk, keyid ); if (pk->flags.primary) c->trustletter = (opt.fast_list_mode ? 0 : get_validity_info (c->ctrl, node->pkt->pkttype == PKT_PUBLIC_KEY ? node : NULL, pk, NULL)); es_printf ("%s:", pk->flags.primary? "pub":"sub" ); if (c->trustletter) es_putc (c->trustletter, es_stdout); es_printf (":%u:%d:%08lX%08lX:%s:%s::", nbits_from_pk( pk ), pk->pubkey_algo, (ulong)keyid[0],(ulong)keyid[1], colon_datestr_from_pk( pk ), colon_strtime (pk->expiredate) ); if (pk->flags.primary && !opt.fast_list_mode) es_putc (get_ownertrust_info (c->ctrl, pk, 1), es_stdout); es_putc (':', es_stdout); es_putc ('\n', es_stdout); } else { print_key_line (c->ctrl, es_stdout, pk, 0); } if (opt.keyid_format == KF_NONE && !opt.with_colons) ; /* Already printed. */ else if ((pk->flags.primary && opt.fingerprint) || opt.fingerprint > 1) print_fingerprint (c->ctrl, NULL, pk, 0); if (pk->flags.primary) { int kl = opt.keyid_format == KF_NONE? 0 : keystrlen (); /* Now list all userids with their signatures. */ for (node = node->next; node; node = node->next) { if (node->pkt->pkttype == PKT_SIGNATURE) { list_node (c, node ); } else if (node->pkt->pkttype == PKT_USER_ID) { if (opt.with_colons) es_printf ("%s:::::::::", node->pkt->pkt.user_id->attrib_data?"uat":"uid"); else es_printf ("uid%*s", kl + (opt.legacy_list_mode? 9:11), "" ); print_userid (node->pkt); if (opt.with_colons) es_putc (':', es_stdout); es_putc ('\n', es_stdout); } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { list_node(c, node ); } } } } else if (node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { log_debug ("FIXME: No way to print secret key packets here\n"); /* fixme: We may use a function to turn a secret key packet into a public key one and use that here. */ } else if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; int is_selfsig = 0; int rc2 = 0; size_t n; char *p; int sigrc = ' '; if (!opt.verbose) return; if (sig->sig_class == 0x20 || sig->sig_class == 0x30) es_fputs ("rev", es_stdout); else es_fputs ("sig", es_stdout); if (opt.check_sigs) { fflush (stdout); rc2 = do_check_sig (c, node, NULL, 0, NULL, &is_selfsig, NULL, NULL, NULL); switch (gpg_err_code (rc2)) { case 0: sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } } else /* Check whether this is a self signature. */ { u32 keyid[2]; if (c->list->pkt->pkttype == PKT_PUBLIC_KEY || c->list->pkt->pkttype == PKT_SECRET_KEY ) { keyid_from_pk (c->list->pkt->pkt.public_key, keyid); if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]) is_selfsig = 1; } } if (opt.with_colons) { es_putc (':', es_stdout); if (sigrc != ' ') es_putc (sigrc, es_stdout); es_printf ("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo, (ulong)sig->keyid[0], (ulong)sig->keyid[1], colon_datestr_from_sig (sig), colon_expirestr_from_sig (sig)); if (sig->trust_depth || sig->trust_value) es_printf ("%d %d",sig->trust_depth,sig->trust_value); es_putc (':', es_stdout); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_putc (':', es_stdout); } else es_printf ("%c %s %s ", sigrc, keystr (sig->keyid), datestr_from_sig(sig)); if (sigrc == '%') es_printf ("[%s] ", gpg_strerror (rc2) ); else if (sigrc == '?') ; else if (is_selfsig) { if (opt.with_colons) es_putc (':', es_stdout); es_fputs (sig->sig_class == 0x18? "[keybind]":"[selfsig]", es_stdout); if (opt.with_colons) es_putc (':', es_stdout); } else if (!opt.fast_list_mode) { p = get_user_id (c->ctrl, sig->keyid, &n, NULL); es_write_sanitized (es_stdout, p, n, opt.with_colons?":":NULL, NULL ); xfree (p); } if (opt.with_colons) es_printf (":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l'); es_putc ('\n', es_stdout); } else log_error ("invalid node with packet of type %d\n", node->pkt->pkttype); } int proc_packets (ctrl_t ctrl, void *anchor, iobuf_t a ) { int rc; CTX c = xmalloc_clear (sizeof *c); c->ctrl = ctrl; c->anchor = anchor; rc = do_proc_packets (c, a); xfree (c); return rc; } int proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a, strlist_t signedfiles, const char *sigfilename ) { CTX c = xmalloc_clear (sizeof *c); int rc; c->ctrl = ctrl; c->anchor = anchor; c->sigs_only = 1; c->signed_data.data_fd = GNUPG_INVALID_FD; c->signed_data.data_names = signedfiles; c->signed_data.used = !!signedfiles; c->sigfilename = sigfilename; rc = do_proc_packets (c, a); /* If we have not encountered any signature we print an error messages, send a NODATA status back and return an error code. Using log_error is required because verify_files does not check error codes for each file but we want to terminate the process with an error. */ if (!rc && !c->any.sig_seen) { write_status_text (STATUS_NODATA, "4"); log_error (_("no signature found\n")); rc = GPG_ERR_NO_DATA; } /* Propagate the signature seen flag upward. Do this only on success so that we won't issue the nodata status several times. */ if (!rc && c->anchor && c->any.sig_seen) c->anchor->any.sig_seen = 1; xfree (c); return rc; } int proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, iobuf_t a, gnupg_fd_t signed_data_fd) { int rc; CTX c; c = xtrycalloc (1, sizeof *c); if (!c) return gpg_error_from_syserror (); c->ctrl = ctrl; c->anchor = anchor; c->sigs_only = 1; c->signed_data.data_fd = signed_data_fd; c->signed_data.data_names = NULL; c->signed_data.used = (signed_data_fd != GNUPG_INVALID_FD); rc = do_proc_packets (c, a); /* If we have not encountered any signature we print an error messages, send a NODATA status back and return an error code. Using log_error is required because verify_files does not check error codes for each file but we want to terminate the process with an error. */ if (!rc && !c->any.sig_seen) { write_status_text (STATUS_NODATA, "4"); log_error (_("no signature found\n")); rc = gpg_error (GPG_ERR_NO_DATA); } /* Propagate the signature seen flag upward. Do this only on success so that we won't issue the nodata status several times. */ if (!rc && c->anchor && c->any.sig_seen) c->anchor->any.sig_seen = 1; xfree ( c ); return rc; } int proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a ) { CTX c = xmalloc_clear (sizeof *c); int rc; c->ctrl = ctrl; c->anchor = anchor; c->encrypt_only = 1; rc = do_proc_packets (c, a); xfree (c); return rc; } static int check_nesting (CTX c) { int level; for (level=0; c; c = c->anchor) level++; if (level > MAX_NESTING_DEPTH) { log_error ("input data with too deeply nested packets\n"); write_status_text (STATUS_UNEXPECTED, "1"); return GPG_ERR_BAD_DATA; } return 0; } static int do_proc_packets (CTX c, iobuf_t a) { PACKET *pkt; struct parse_packet_ctx_s parsectx; int rc = 0; int any_data = 0; int newpkt; rc = check_nesting (c); if (rc) return rc; pkt = xmalloc( sizeof *pkt ); c->iobuf = a; init_packet(pkt); init_parse_packet (&parsectx, a); while ((rc=parse_packet (&parsectx, pkt)) != -1) { any_data = 1; if (rc) { free_packet (pkt, &parsectx); /* Stop processing when an invalid packet has been encountered * but don't do so when we are doing a --list-packets. */ if (gpg_err_code (rc) == GPG_ERR_INV_PACKET && opt.list_packets == 0) break; continue; } newpkt = -1; if (opt.list_packets) { switch (pkt->pkttype) { case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_AEAD:proc_encrypted (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; default: newpkt = 0; break; } } else if (c->sigs_only) { switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_USER_ID: case PKT_SYMKEY_ENC: case PKT_PUBKEY_ENC: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_AEAD: write_status_text( STATUS_UNEXPECTED, "0" ); rc = GPG_ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break; default: newpkt = 0; break; } } else if (c->encrypt_only) { switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: case PKT_USER_ID: write_status_text (STATUS_UNEXPECTED, "0"); rc = GPG_ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; case PKT_GPG_CONTROL: newpkt = add_gpg_control (c, pkt); break; default: newpkt = 0; break; } } else { switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: release_list (c); c->list = new_kbnode (pkt); newpkt = 1; break; case PKT_PUBLIC_SUBKEY: case PKT_SECRET_SUBKEY: newpkt = add_subkey (c, pkt); break; case PKT_USER_ID: newpkt = add_user_id (c, pkt); break; case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break; case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break; case PKT_RING_TRUST: newpkt = add_ring_trust (c, pkt); break; default: newpkt = 0; break; } } if (rc) goto leave; /* This is a very ugly construct and frankly, I don't remember why * I used it. Adding the MDC check here is a hack. * The right solution is to initiate another context for encrypted * packet and not to reuse the current one ... It works right * when there is a compression packet between which adds just * an extra layer. * Hmmm: Rewrite this whole module here?? */ if (pkt->pkttype != PKT_SIGNATURE && pkt->pkttype != PKT_MDC) c->any.data = (pkt->pkttype == PKT_PLAINTEXT); if (newpkt == -1) ; else if (newpkt) { pkt = xmalloc (sizeof *pkt); init_packet (pkt); } else free_packet (pkt, &parsectx); } if (rc == GPG_ERR_INV_PACKET) write_status_text (STATUS_NODATA, "3"); if (any_data) rc = 0; else if (rc == -1) write_status_text (STATUS_NODATA, "2"); leave: release_list (c); xfree(c->dek); free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree (pkt); free_md_filter_context (&c->mfx); return rc; } /* Return true if the AKL has the WKD method specified. */ static int akl_has_wkd_method (void) { struct akl *akl; for (akl = opt.auto_key_locate; akl; akl = akl->next) if (akl->type == AKL_WKD) return 1; return 0; } /* Return the ISSUER fingerprint buffer and its length at R_LEN. * Returns NULL if not available. The returned buffer is valid as * long as SIG is not modified. */ const byte * issuer_fpr_raw (PKT_signature *sig, size_t *r_len) { const byte *p; size_t n; p = parse_sig_subpkt (sig, 1, SIGSUBPKT_ISSUER_FPR, &n); if (p && ((n == 21 && p[0] == 4) || (n == 33 && p[0] == 5))) { *r_len = n - 1; return p+1; } *r_len = 0; return NULL; } /* Return the ISSUER fingerprint string in human readable format if * available. Caller must release the string. */ /* FIXME: Move to another file. */ char * issuer_fpr_string (PKT_signature *sig) { const byte *p; size_t n; p = issuer_fpr_raw (sig, &n); return p? bin2hex (p, n, NULL) : NULL; } static void print_good_bad_signature (int statno, const char *keyid_str, kbnode_t un, PKT_signature *sig, int rc) { char *p; write_status_text_and_buffer (statno, keyid_str, un? un->pkt->pkt.user_id->name:"[?]", un? un->pkt->pkt.user_id->len:3, -1); if (un) p = utf8_to_native (un->pkt->pkt.user_id->name, un->pkt->pkt.user_id->len, 0); else p = xstrdup ("[?]"); if (rc) log_info (_("BAD signature from \"%s\""), p); else if (sig->flags.expired) log_info (_("Expired signature from \"%s\""), p); else log_info (_("Good signature from \"%s\""), p); xfree (p); } static int check_sig_and_print (CTX c, kbnode_t node) { PKT_signature *sig = node->pkt->pkt.signature; const char *astr; gpg_error_t rc; int is_expkey = 0; int is_revkey = 0; char *issuer_fpr = NULL; PKT_public_key *pk = NULL; /* The public key for the signature or NULL. */ const void *extrahash = NULL; size_t extrahashlen = 0; kbnode_t included_keyblock = NULL; char pkstrbuf[PUBKEY_STRING_SIZE] = { 0 }; if (opt.skip_verify) { log_info(_("signature verification suppressed\n")); return 0; } /* Check that the message composition is valid. * * Per RFC-2440bis (-15) allowed: * * S{1,n} -- detached signature. * S{1,n} P -- old style PGP2 signature * O{1,n} P S{1,n} -- standard OpenPGP signature. * C P S{1,n} -- cleartext signature. * * * O = One-Pass Signature packet. * S = Signature packet. * P = OpenPGP Message packet (Encrypted | Compressed | Literal) * (Note that the current rfc2440bis draft also allows * for a signed message but that does not work as it * introduces ambiguities.) * We keep track of these packages using the marker packet * CTRLPKT_PLAINTEXT_MARK. * C = Marker packet for cleartext signatures. * * We reject all other messages. * * Actually we are calling this too often, i.e. for verification of * each message but better have some duplicate work than to silently * introduce a bug here. */ { kbnode_t n; int n_onepass, n_sig; /* log_debug ("checking signature packet composition\n"); */ /* dump_kbnode (c->list); */ n = c->list; log_assert (n); if ( n->pkt->pkttype == PKT_SIGNATURE ) { /* This is either "S{1,n}" case (detached signature) or "S{1,n} P" (old style PGP2 signature). */ for (n = n->next; n; n = n->next) if (n->pkt->pkttype != PKT_SIGNATURE) break; if (!n) ; /* Okay, this is a detached signature. */ else if (n->pkt->pkttype == PKT_GPG_CONTROL && (n->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK) ) { if (n->next) goto ambiguous; /* We only allow one P packet. */ extrahash = n->pkt->pkt.gpg_control->data; extrahashlen = n->pkt->pkt.gpg_control->datalen; } else goto ambiguous; } else if (n->pkt->pkttype == PKT_ONEPASS_SIG) { /* This is the "O{1,n} P S{1,n}" case (standard signature). */ for (n_onepass=1, n = n->next; n && n->pkt->pkttype == PKT_ONEPASS_SIG; n = n->next) n_onepass++; if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL && (n->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK))) goto ambiguous; extrahash = n->pkt->pkt.gpg_control->data; extrahashlen = n->pkt->pkt.gpg_control->datalen; for (n_sig=0, n = n->next; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) n_sig++; if (!n_sig) goto ambiguous; /* If we wanted to disallow multiple sig verification, we'd do * something like this: * * if (n) * goto ambiguous; * * However, this can stay allowable as we can't get here. */ if (n_onepass != n_sig) { log_info ("number of one-pass packets does not match " "number of signature packets\n"); goto ambiguous; } } else if (n->pkt->pkttype == PKT_GPG_CONTROL && n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) { /* This is the "C P S{1,n}" case (clear text signature). */ n = n->next; if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL && (n->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK))) goto ambiguous; extrahash = n->pkt->pkt.gpg_control->data; extrahashlen = n->pkt->pkt.gpg_control->datalen; for (n_sig=0, n = n->next; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) n_sig++; if (n || !n_sig) goto ambiguous; } else { ambiguous: log_error(_("can't handle this ambiguous signature data\n")); return 0; } } /* End checking signature packet composition. */ if (sig->signers_uid) write_status_buffer (STATUS_NEWSIG, sig->signers_uid, strlen (sig->signers_uid), 0); else write_status_text (STATUS_NEWSIG, NULL); astr = openpgp_pk_algo_name ( sig->pubkey_algo ); issuer_fpr = issuer_fpr_string (sig); if (issuer_fpr) { log_info (_("Signature made %s\n"), asctimestamp(sig->timestamp)); log_info (_(" using %s key %s\n"), astr? astr: "?", issuer_fpr); } else if (!keystrlen () || keystrlen () > 8) { log_info (_("Signature made %s\n"), asctimestamp(sig->timestamp)); log_info (_(" using %s key %s\n"), astr? astr: "?", keystr(sig->keyid)); } else /* Legacy format. */ log_info (_("Signature made %s using %s key ID %s\n"), asctimestamp(sig->timestamp), astr? astr: "?", keystr(sig->keyid)); /* In verbose mode print the signers UID. */ if (sig->signers_uid) log_info (_(" issuer \"%s\"\n"), sig->signers_uid); rc = do_check_sig (c, node, extrahash, extrahashlen, NULL, NULL, &is_expkey, &is_revkey, &pk); /* If the key is not found but the signature includes a key block we * use that key block for verification and on success import it. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && sig->flags.key_block && opt.flags.auto_key_import) { PKT_public_key *included_pk; const byte *kblock; size_t kblock_len; included_pk = xcalloc (1, sizeof *included_pk); kblock = parse_sig_subpkt (sig, 1, SIGSUBPKT_KEY_BLOCK, &kblock_len); if (kblock && kblock_len > 1 && !get_pubkey_from_buffer (c->ctrl, included_pk, kblock+1, kblock_len-1, sig->keyid, &included_keyblock)) { rc = do_check_sig (c, node, extrahash, extrahashlen, included_pk, NULL, &is_expkey, &is_revkey, &pk); if (opt.verbose) log_debug ("checked signature using included key block: %s\n", gpg_strerror (rc)); if (!rc) { /* The keyblock has been verified, we now import it. */ rc = import_included_key_block (c->ctrl, included_keyblock); } } free_public_key (included_pk); } /* If the key isn't found, check for a preferred keyserver. Note * that this is only done if honor-keyserver-url has been set. We * test for this in the loop so that we can show info about the * preferred keyservers. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && sig->flags.pref_ks) { const byte *p; int seq = 0; size_t n; int any_pref_ks = 0; while ((p=enum_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, &n, &seq, NULL))) { /* According to my favorite copy editor, in English grammar, you say "at" if the key is located on a web page, but "from" if it is located on a keyserver. I'm not going to even try to make two strings here :) */ log_info(_("Key available at: ") ); print_utf8_buffer (log_get_stream(), p, n); log_printf ("\n"); any_pref_ks = 1; if ((opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) && (opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL)) { struct keyserver_spec *spec; spec = parse_preferred_keyserver (sig); if (spec) { int res; if (DBG_LOOKUP) log_debug ("trying auto-key-retrieve method %s\n", "Pref-KS"); free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_keyid (c->ctrl, sig->keyid,spec, KEYSERVER_IMPORT_FLAG_QUICK); glo_ctrl.in_auto_key_retrieve--; if (!res) rc = do_check_sig (c, node, extrahash, extrahashlen, NULL, NULL, &is_expkey, &is_revkey, &pk); else if (DBG_LOOKUP) log_debug ("lookup via %s failed: %s\n", "Pref-KS", gpg_strerror (res)); free_keyserver_spec (spec); if (!rc) break; } } } if (any_pref_ks && (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) && !(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL)) log_info (_("Note: Use '%s' to make use of this info\n"), "--keyserver-option honor-keyserver-url"); } /* If the above methods didn't work, our next try is to retrieve the * key from the WKD. This requires that WKD is in the AKL and the * Signer's UID is in the signature. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) && !opt.flags.disable_signer_uid && akl_has_wkd_method () && sig->signers_uid) { int res; if (DBG_LOOKUP) log_debug ("trying auto-key-retrieve method %s\n", "WKD"); free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_wkd (c->ctrl, sig->signers_uid, KEYSERVER_IMPORT_FLAG_QUICK, NULL, NULL); glo_ctrl.in_auto_key_retrieve--; /* Fixme: If the fingerprint is embedded in the signature, * compare it to the fingerprint of the returned key. */ if (!res) rc = do_check_sig (c, node, extrahash, extrahashlen, NULL, NULL, &is_expkey, &is_revkey, &pk); else if (DBG_LOOKUP) log_debug ("lookup via %s failed: %s\n", "WKD", gpg_strerror (res)); } /* If the above methods didn't work, our next try is to locate * the key via its fingerprint from a keyserver. This requires * that the signers fingerprint is encoded in the signature. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) && keyserver_any_configured (c->ctrl)) { int res; const byte *p; size_t n; p = issuer_fpr_raw (sig, &n); if (p) { if (DBG_LOOKUP) log_debug ("trying auto-key-retrieve method %s\n", "KS"); /* v4 or v5 packet with a SHA-1/256 fingerprint. */ free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_fprint (c->ctrl, p, n, opt.keyserver, KEYSERVER_IMPORT_FLAG_QUICK); glo_ctrl.in_auto_key_retrieve--; if (!res) rc = do_check_sig (c, node, extrahash, extrahashlen, NULL, NULL, &is_expkey, &is_revkey, &pk); else if (DBG_LOOKUP) log_debug ("lookup via %s failed: %s\n", "KS", gpg_strerror (res)); } } /* Do do something with the result of the signature checking. */ if (!rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE) { /* We have checked the signature and the result is either a good * signature or a bad signature. Further examination follows. */ kbnode_t un, keyblock; int count = 0; int keyblock_has_pk = 0; /* For failsafe check. */ int statno; char keyid_str[50]; PKT_public_key *mainpk = NULL; if (rc) statno = STATUS_BADSIG; else if (sig->flags.expired) statno = STATUS_EXPSIG; else if (is_expkey) statno = STATUS_EXPKEYSIG; else if(is_revkey) statno = STATUS_REVKEYSIG; else statno = STATUS_GOODSIG; /* FIXME: We should have the public key in PK and thus the * keyblock has already been fetched. Thus we could use the * fingerprint or PK itself to lookup the entire keyblock. That * would best be done with a cache. */ if (included_keyblock) { keyblock = included_keyblock; included_keyblock = NULL; } else keyblock = get_pubkeyblock_for_sig (c->ctrl, sig); snprintf (keyid_str, sizeof keyid_str, "%08lX%08lX [uncertain] ", (ulong)sig->keyid[0], (ulong)sig->keyid[1]); /* Find and print the primary user ID along with the "Good|Expired|Bad signature" line. */ for (un=keyblock; un; un = un->next) { int valid; if (!keyblock_has_pk && (un->pkt->pkttype == PKT_PUBLIC_KEY || un->pkt->pkttype == PKT_PUBLIC_SUBKEY) && !cmp_public_keys (un->pkt->pkt.public_key, pk)) { keyblock_has_pk = 1; } if (un->pkt->pkttype == PKT_PUBLIC_KEY) { mainpk = un->pkt->pkt.public_key; continue; } if (un->pkt->pkttype != PKT_USER_ID) continue; if (!un->pkt->pkt.user_id->created) continue; if (un->pkt->pkt.user_id->flags.revoked) continue; if (un->pkt->pkt.user_id->flags.expired) continue; if (!un->pkt->pkt.user_id->flags.primary) continue; /* We want the textual primary user ID here */ if (un->pkt->pkt.user_id->attrib_data) continue; log_assert (mainpk); /* Since this is just informational, don't actually ask the user to update any trust information. (Note: we register the signature later.) Because print_good_bad_signature does not print a LF we need to compute the validity before calling that function. */ if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY)) valid = get_validity (c->ctrl, keyblock, mainpk, un->pkt->pkt.user_id, NULL, 0); else valid = 0; /* Not used. */ keyid_str[17] = 0; /* cut off the "[uncertain]" part */ print_good_bad_signature (statno, keyid_str, un, sig, rc); if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY)) log_printf (" [%s]\n",trust_value_to_string(valid)); else log_printf ("\n"); count++; /* At this point we could in theory stop because the primary * UID flag is never set for more than one User ID per * keyblock. However, we use this loop also for a failsafe * check that the public key used to create the signature is * contained in the keyring.*/ } log_assert (mainpk); if (!keyblock_has_pk) { log_error ("signature key lost from keyblock\n"); rc = gpg_error (GPG_ERR_INTERNAL); } /* In case we did not found a valid textual userid above we print the first user id packet or a "[?]" instead along with the "Good|Expired|Bad signature" line. */ if (!count) { /* Try for an invalid textual userid */ for (un=keyblock; un; un = un->next) { if (un->pkt->pkttype == PKT_USER_ID && !un->pkt->pkt.user_id->attrib_data) break; } /* Try for any userid at all */ if (!un) { for (un=keyblock; un; un = un->next) { if (un->pkt->pkttype == PKT_USER_ID) break; } } if (opt.trust_model==TM_ALWAYS || !un) keyid_str[17] = 0; /* cut off the "[uncertain]" part */ print_good_bad_signature (statno, keyid_str, un, sig, rc); if (opt.trust_model != TM_ALWAYS && un) log_printf (" %s",_("[uncertain]") ); log_printf ("\n"); } /* If we have a good signature and already printed * the primary user ID, print all the other user IDs */ if (count && !rc && !(opt.verify_options & VERIFY_SHOW_PRIMARY_UID_ONLY)) { char *p; for( un=keyblock; un; un = un->next) { if (un->pkt->pkttype != PKT_USER_ID) continue; if ((un->pkt->pkt.user_id->flags.revoked || un->pkt->pkt.user_id->flags.expired) && !(opt.verify_options & VERIFY_SHOW_UNUSABLE_UIDS)) continue; /* Skip textual primary user ids which we printed above. */ if (un->pkt->pkt.user_id->flags.primary && !un->pkt->pkt.user_id->attrib_data ) continue; /* If this user id has attribute data, print that. */ if (un->pkt->pkt.user_id->attrib_data) { dump_attribs (un->pkt->pkt.user_id, mainpk); if (opt.verify_options&VERIFY_SHOW_PHOTOS) show_photos (c->ctrl, un->pkt->pkt.user_id->attribs, un->pkt->pkt.user_id->numattribs, mainpk ,un->pkt->pkt.user_id); } p = utf8_to_native (un->pkt->pkt.user_id->name, un->pkt->pkt.user_id->len, 0); log_info (_(" aka \"%s\""), p); xfree (p); if ((opt.verify_options & VERIFY_SHOW_UID_VALIDITY)) { const char *valid; if (un->pkt->pkt.user_id->flags.revoked) valid = _("revoked"); else if (un->pkt->pkt.user_id->flags.expired) valid = _("expired"); else /* Since this is just informational, don't actually ask the user to update any trust information. */ valid = (trust_value_to_string (get_validity (c->ctrl, keyblock, mainpk, un->pkt->pkt.user_id, NULL, 0))); log_printf (" [%s]\n",valid); } else log_printf ("\n"); } } /* For good signatures print notation data. */ if (!rc) { if ((opt.verify_options & VERIFY_SHOW_POLICY_URLS)) show_policy_url (sig, 0, 1); else show_policy_url (sig, 0, 2); if ((opt.verify_options & VERIFY_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 0, 1); else show_keyserver_url (sig, 0, 2); if ((opt.verify_options & VERIFY_SHOW_NOTATIONS)) show_notation (sig, 0, 1, (((opt.verify_options&VERIFY_SHOW_STD_NOTATIONS)?1:0) + ((opt.verify_options&VERIFY_SHOW_USER_NOTATIONS)?2:0))); else show_notation (sig, 0, 2, 0); } /* Fill PKSTRBUF with the algostring in case we later need it. */ if (pk) pubkey_string (pk, pkstrbuf, sizeof pkstrbuf); /* For good signatures print the VALIDSIG status line. */ if (!rc && (is_status_enabled () || opt.assert_signer_list || opt.assert_pubkey_algos) && pk) { char pkhex[MAX_FINGERPRINT_LEN*2+1]; char mainpkhex[MAX_FINGERPRINT_LEN*2+1]; hexfingerprint (pk, pkhex, sizeof pkhex); hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex); /* TODO: Replace the reserved '0' in the field below with bits for status flags (policy url, notation, etc.). */ write_status_printf (STATUS_VALIDSIG, "%s %s %lu %lu %d 0 %d %d %02X %s", pkhex, strtimestamp (sig->timestamp), (ulong)sig->timestamp, (ulong)sig->expiredate, sig->version, sig->pubkey_algo, sig->digest_algo, sig->sig_class, mainpkhex); /* Handle the --assert-signer option. */ check_assert_signer_list (mainpkhex, pkhex); /* Handle the --assert-pubkey-algo option. */ check_assert_pubkey_algo (pkstrbuf, pkhex); } /* Print compliance warning for Good signatures. */ if (!rc && pk && !opt.quiet && !gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0, pk->pkey, nbits_from_pk (pk), NULL)) { log_info (_("WARNING: This key is not suitable for signing" " in %s mode\n"), gnupg_compliance_option_string (opt.compliance)); } /* For good signatures compute and print the trust information. Note that in the Tofu trust model this may ask the user on how to resolve a conflict. */ if (!rc) { rc = check_signatures_trust (c->ctrl, keyblock, pk, sig); } /* Print extra information about the signature. */ if (sig->flags.expired) { log_info (_("Signature expired %s\n"), asctimestamp(sig->expiredate)); if (!rc) rc = gpg_error (GPG_ERR_GENERAL); /* Need a better error here? */ } else if (sig->expiredate) log_info (_("Signature expires %s\n"), asctimestamp(sig->expiredate)); if (opt.verbose) { log_info (_("%s signature, digest algorithm %s%s%s\n"), sig->sig_class==0x00?_("binary"): sig->sig_class==0x01?_("textmode"):_("unknown"), gcry_md_algo_name (sig->digest_algo), *pkstrbuf?_(", key algorithm "):"", pkstrbuf); } /* Print final warnings. */ if (!rc && !c->signed_data.used) { /* Signature is basically good but we test whether the deprecated command gpg --verify FILE.sig was used instead of gpg --verify FILE.sig FILE to verify a detached signature. If we figure out that a data file with a matching name exists, we print a warning. The problem is that the first form would also verify a standard signature. This behavior could be used to create a made up .sig file for a tarball by creating a standard signature from a valid detached signature packet (for example from a signed git tag). Then replace the sig file on the FTP server along with a changed tarball. Using the first form the verify command would correctly verify the signature but don't even consider the tarball. */ kbnode_t n; char *dfile; dfile = get_matching_datafile (c->sigfilename); if (dfile) { for (n = c->list; n; n = n->next) if (n->pkt->pkttype != PKT_SIGNATURE) break; if (n) { /* Not only signature packets in the tree thus this is not a detached signature. */ log_info (_("WARNING: not a detached signature; " "file '%s' was NOT verified!\n"), dfile); assert_signer_true = 0; } xfree (dfile); } } /* Compute compliance with CO_DE_VS. */ if (pk && gnupg_gcrypt_is_compliant (CO_DE_VS) && gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey, nbits_from_pk (pk), NULL) && gnupg_digest_is_compliant (CO_DE_VS, sig->digest_algo)) write_status_strings (STATUS_VERIFICATION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS), NULL); else if (opt.flags.require_compliance && opt.compliance == CO_DE_VS) { log_error (_("operation forced to fail due to" " unfulfilled compliance rules\n")); if (!rc) rc = gpg_error (GPG_ERR_FORBIDDEN); } free_public_key (pk); pk = NULL; release_kbnode( keyblock ); if (rc) g10_errors_seen = 1; } else /* Error checking the signature. (neither Good nor Bad). */ { write_status_printf (STATUS_ERRSIG, "%08lX%08lX %d %d %02x %lu %d %s", (ulong)sig->keyid[0], (ulong)sig->keyid[1], sig->pubkey_algo, sig->digest_algo, sig->sig_class, (ulong)sig->timestamp, gpg_err_code (rc), issuer_fpr? issuer_fpr:"-"); if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY) { write_status_printf (STATUS_NO_PUBKEY, "%08lX%08lX", (ulong)sig->keyid[0], (ulong)sig->keyid[1]); } if (gpg_err_code (rc) != GPG_ERR_NOT_PROCESSED) log_error (_("Can't check signature: %s\n"), gpg_strerror (rc)); } free_public_key (pk); release_kbnode (included_keyblock); xfree (issuer_fpr); return rc; } /* * Process the tree which starts at node */ static void proc_tree (CTX c, kbnode_t node) { kbnode_t n1; int rc; if (opt.list_packets || opt.list_only) return; /* We must skip our special plaintext marker packets here because they may be the root packet. These packets are only used in additional checks and skipping them here doesn't matter. */ while (node && node->pkt->pkttype == PKT_GPG_CONTROL && node->pkt->pkt.gpg_control->control == CTRLPKT_PLAINTEXT_MARK) { node = node->next; } if (!node) return; c->trustletter = ' '; if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { merge_keys_and_selfsig (c->ctrl, node); list_node (c, node); } else if (node->pkt->pkttype == PKT_SECRET_KEY) { merge_keys_and_selfsig (c->ctrl, node); list_node (c, node); } else if (node->pkt->pkttype == PKT_ONEPASS_SIG) { /* Check all signatures. */ if (!c->any.data) { int use_textmode = 0; free_md_filter_context (&c->mfx); /* Prepare to create all requested message digests. */ rc = gcry_md_open (&c->mfx.md, 0, 0); if (rc) goto hash_err; /* Fixme: why looking for the signature packet and not the one-pass packet? */ for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) gcry_md_enable (c->mfx.md, n1->pkt->pkt.signature->digest_algo); if (n1 && n1->pkt->pkt.onepass_sig->sig_class == 0x01) use_textmode = 1; /* Ask for file and hash it. */ if (c->sigs_only) { if (c->signed_data.used && c->signed_data.data_fd != GNUPG_INVALID_FD) rc = hash_datafile_by_fd (c->mfx.md, NULL, c->signed_data.data_fd, use_textmode); else rc = hash_datafiles (c->mfx.md, NULL, c->signed_data.data_names, c->sigfilename, use_textmode); } else { rc = ask_for_detached_datafile (c->mfx.md, NULL, iobuf_get_real_fname (c->iobuf), use_textmode); } hash_err: if (rc) { log_error ("can't hash datafile: %s\n", gpg_strerror (rc)); return; } } else if (c->signed_data.used) { log_error (_("not a detached signature\n")); return; } for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) if (check_sig_and_print (c, n1) && opt.batch) break; } else if (node->pkt->pkttype == PKT_GPG_CONTROL && node->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START) { /* Clear text signed message. */ if (!c->any.data) { log_error ("cleartext signature without data\n"); return; } else if (c->signed_data.used) { log_error (_("not a detached signature\n")); return; } for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) if (check_sig_and_print (c, n1) && opt.batch) break; } else if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; int multiple_ok = 1; n1 = find_next_kbnode (node, PKT_SIGNATURE); if (n1) { byte class = sig->sig_class; byte hash = sig->digest_algo; for (; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE))) { /* We can't currently handle multiple signatures of * different classes (we'd pretty much have to run a * different hash context for each), but if they are all * the same and it is detached signature, we make an * exception. Note that the old code also disallowed * multiple signatures if the digest algorithms are * different. We softened this restriction only for * detached signatures, to be on the safe side. */ if (n1->pkt->pkt.signature->sig_class != class || (c->any.data && n1->pkt->pkt.signature->digest_algo != hash)) { multiple_ok = 0; log_info (_("WARNING: multiple signatures detected. " "Only the first will be checked.\n")); break; } } } if (sig->sig_class != 0x00 && sig->sig_class != 0x01) { log_info(_("standalone signature of class 0x%02x\n"), sig->sig_class); } else if (!c->any.data) { /* Detached signature */ free_md_filter_context (&c->mfx); rc = gcry_md_open (&c->mfx.md, sig->digest_algo, 0); if (rc) goto detached_hash_err; if (multiple_ok) { /* If we have and want to handle multiple signatures we * need to enable all hash algorithms for the context. */ for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE)); ) if (!openpgp_md_test_algo (n1->pkt->pkt.signature->digest_algo)) gcry_md_enable (c->mfx.md, map_md_openpgp_to_gcry (n1->pkt->pkt.signature->digest_algo)); } if (RFC2440 || RFC4880) ; /* Strict RFC mode. */ else if (sig->digest_algo == DIGEST_ALGO_SHA1 && sig->pubkey_algo == PUBKEY_ALGO_DSA && sig->sig_class == 0x01) { /* Enable a workaround for a pgp5 bug when the detached * signature has been created in textmode. Note that we * do not implement this for multiple signatures with * different hash algorithms. */ rc = gcry_md_open (&c->mfx.md2, sig->digest_algo, 0); if (rc) goto detached_hash_err; } /* Here we used to have another hack to work around a pgp * 2 bug: It worked by not using the textmode for detached * signatures; this would let the first signature check * (on md) fail but the second one (on md2), which adds an * extra CR would then have produced the "correct" hash. * This is very, very ugly hack but it may haved help in * some cases (and break others). * c->mfx.md2? 0 :(sig->sig_class == 0x01) */ if (DBG_HASHING) { gcry_md_debug (c->mfx.md, "verify"); if (c->mfx.md2) gcry_md_debug (c->mfx.md2, "verify2"); } if (c->sigs_only) { if (c->signed_data.used && c->signed_data.data_fd != GNUPG_INVALID_FD) rc = hash_datafile_by_fd (c->mfx.md, c->mfx.md2, c->signed_data.data_fd, (sig->sig_class == 0x01)); else rc = hash_datafiles (c->mfx.md, c->mfx.md2, c->signed_data.data_names, c->sigfilename, (sig->sig_class == 0x01)); } else { rc = ask_for_detached_datafile (c->mfx.md, c->mfx.md2, iobuf_get_real_fname(c->iobuf), (sig->sig_class == 0x01)); } detached_hash_err: if (rc) { log_error ("can't hash datafile: %s\n", gpg_strerror (rc)); return; } } else if (c->signed_data.used) { log_error (_("not a detached signature\n")); return; } else if (!opt.quiet) log_info (_("old style (PGP 2.x) signature\n")); if (multiple_ok) { for (n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE))) if (check_sig_and_print (c, n1) && opt.batch) break; } else check_sig_and_print (c, node); } else { dump_kbnode (c->list); log_error ("invalid root packet detected in proc_tree()\n"); dump_kbnode (node); } } diff --git a/g10/misc.c b/g10/misc.c index 432fe1760..ce0c04b3b 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,1948 +1,1950 @@ /* misc.c - miscellaneous functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * Copyright (C) 2014 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 #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include #include #endif #ifdef HAVE_SETRLIMIT #include #include #include #endif #ifdef ENABLE_SELINUX_HACKS #include #endif #ifdef HAVE_W32_SYSTEM #include #include #ifdef HAVE_WINSOCK2_H # define WIN32_LEAN_AND_MEAN 1 # include #endif #include #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #endif /*HAVE_W32_SYSTEM*/ #include "gpg.h" #ifdef HAVE_W32_SYSTEM # include "../common/status.h" #endif /*HAVE_W32_SYSTEM*/ #include "../common/util.h" #include "main.h" #include "photoid.h" #include "options.h" #include "call-agent.h" #include "../common/i18n.h" #include "../common/zb32.h" /* FIXME: Libgcrypt 1.9 will support EAX. Until we name this a * requirement we hardwire the enum used for EAX. */ #define MY_GCRY_CIPHER_MODE_EAX 14 #ifdef ENABLE_SELINUX_HACKS /* A object and a global variable to keep track of files marked as secured. */ struct secured_file_item { struct secured_file_item *next; ino_t ino; dev_t dev; }; static struct secured_file_item *secured_files; #endif /*ENABLE_SELINUX_HACKS*/ /* For the sake of SELinux we want to restrict access through gpg to certain files we keep under our own control. This function registers such a file and is_secured_file may then be used to check whether a file has ben registered as secured. */ void register_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; /* Note that we stop immediately if something goes wrong here. */ if (gnupg_stat (fname, &buf)) log_fatal (_("fstat of '%s' failed in %s: %s\n"), fname, "register_secured_file", strerror (errno)); /* log_debug ("registering '%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return; /* Already registered. */ } sf = xmalloc (sizeof *sf); sf->ino = buf.st_ino; sf->dev = buf.st_dev; sf->next = secured_files; secured_files = sf; #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ } /* Remove a file registered as secure. */ void unregister_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf, *sfprev; if (gnupg_stat (fname, &buf)) { log_error (_("fstat of '%s' failed in %s: %s\n"), fname, "unregister_secured_file", strerror (errno)); return; } /* log_debug ("unregistering '%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sfprev=NULL,sf=secured_files; sf; sfprev=sf, sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) { if (sfprev) sfprev->next = sf->next; else secured_files = sf->next; xfree (sf); return; } } #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ } /* Return true if FD is corresponds to a secured file. Using -1 for FS is allowed and will return false. */ int is_secured_file (gnupg_fd_t fd) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (fd == -1) return 0; /* No file descriptor so it can't be secured either. */ /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (fstat (fd, &buf)) { log_error (_("fstat(%d) failed in %s: %s\n"), fd, "is_secured_file", strerror (errno)); return 1; } /* log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #else /*!ENABLE_SELINUX_HACKS*/ (void)fd; #endif /*!ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } /* Return true if FNAME is corresponds to a secured file. Using NULL, "" or "-" for FS is allowed and will return false. This function is used before creating a file, thus it won't fail if the file does not exist. */ int is_secured_filename (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (iobuf_is_pipe_filename (fname) || !*fname) return 0; /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (gnupg_stat (fname, &buf)) { if (errno == ENOENT || errno == EPERM || errno == EACCES) return 0; log_error (_("fstat of '%s' failed in %s: %s\n"), fname, "is_secured_filename", strerror (errno)); return 1; } /* log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } u16 checksum_u16( unsigned n ) { u16 a; a = (n >> 8) & 0xff; a += n & 0xff; return a; } u16 checksum (const byte *p, unsigned n) { u16 a; for(a=0; n; n-- ) a += *p++; return a; } u16 checksum_mpi (gcry_mpi_t a) { u16 csum; byte *buffer; size_t nbytes; /* * This code can be skipped when gcry_mpi_print * supports opaque MPI. */ if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { const byte *p; unsigned int nbits; p = gcry_mpi_get_opaque (a, &nbits); if (!p) return 0; csum = nbits >> 8; csum += (nbits & 0xff); csum += checksum (p, (nbits+7)/8); return csum; } if ( gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a) ) BUG (); /* Fixme: For numbers not in secure memory we should use a stack * based buffer and only allocate a larger one if mpi_print returns * an error. */ buffer = (gcry_is_secure(a)? gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes)); if ( gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a) ) BUG (); csum = checksum (buffer, nbytes); xfree (buffer); return csum; } void print_pubkey_algo_note (pubkey_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental public key algorithm %s\n"), openpgp_pk_algo_name (algo)); } } else if (algo == PUBKEY_ALGO_ELGAMAL) { es_fflush (es_stdout); log_info (_("WARNING: Elgamal sign+encrypt keys are deprecated\n")); } } void print_cipher_algo_note (cipher_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental cipher algorithm %s\n"), openpgp_cipher_algo_name (algo)); } } } void print_digest_algo_note (digest_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; const enum gcry_md_algos galgo = map_md_openpgp_to_gcry (algo); if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental digest algorithm %s\n"), gcry_md_algo_name (galgo)); } } else if (is_weak_digest (algo)) { const enum gcry_md_algos galgo = map_md_openpgp_to_gcry (algo); es_fflush (es_stdout); log_info (_("WARNING: digest algorithm %s is deprecated\n"), gcry_md_algo_name (galgo)); } } void print_digest_rejected_note (enum gcry_md_algos algo) { struct weakhash* weak; int show = 1; if (opt.quiet) return; for (weak = opt.weak_digests; weak; weak = weak->next) if (weak->algo == algo) { if (weak->rejection_shown) show = 0; else weak->rejection_shown = 1; break; } if (show) { es_fflush (es_stdout); log_info (_("Note: signatures using the %s algorithm are rejected\n"), gcry_md_algo_name(algo)); } } void print_sha1_keysig_rejected_note (void) { static int shown; if (shown || opt.quiet) return; shown = 1; es_fflush (es_stdout); log_info (_("Note: third-party key signatures using" " the %s algorithm are rejected\n"), gcry_md_algo_name (GCRY_MD_SHA1)); if (!opt.quiet) log_info (_("(use option \"%s\" to override)\n"), "--allow-weak-key-signatures"); } /* Print a message * "(reported error: %s)\n * in verbose mode to further explain an error. If the error code has * the value IGNORE_EC no message is printed. A message is also not * printed if ERR is 0. */ void print_reported_error (gpg_error_t err, gpg_err_code_t ignore_ec) { if (!opt.verbose) return; if (!gpg_err_code (err)) ; else if (gpg_err_code (err) == ignore_ec) ; else if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) log_info (_("(reported error: %s)\n"), gpg_strerror (err)); else log_info (_("(reported error: %s <%s>)\n"), gpg_strerror (err), gpg_strsource (err)); } /* Print a message * "(further info: %s)\n * in verbose mode to further explain an error. That message is * intended to help debug a problem and should not be translated. */ void print_further_info (const char *format, ...) { va_list arg_ptr; if (!opt.verbose) return; log_info (_("(further info: ")); va_start (arg_ptr, format); log_logv (GPGRT_LOGLVL_CONT, format, arg_ptr); va_end (arg_ptr); log_printf (")\n"); } /* Map OpenPGP algo numbers to those used by Libgcrypt. We need to do this for algorithms we implemented in Libgcrypt after they become part of OpenPGP. */ enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo) { switch (algo) { case CIPHER_ALGO_NONE: return GCRY_CIPHER_NONE; #ifdef GPG_USE_IDEA case CIPHER_ALGO_IDEA: return GCRY_CIPHER_IDEA; #else case CIPHER_ALGO_IDEA: return 0; #endif case CIPHER_ALGO_3DES: return GCRY_CIPHER_3DES; #ifdef GPG_USE_CAST5 case CIPHER_ALGO_CAST5: return GCRY_CIPHER_CAST5; #else case CIPHER_ALGO_CAST5: return 0; #endif #ifdef GPG_USE_BLOWFISH case CIPHER_ALGO_BLOWFISH: return GCRY_CIPHER_BLOWFISH; #else case CIPHER_ALGO_BLOWFISH: return 0; #endif #ifdef GPG_USE_AES128 case CIPHER_ALGO_AES: return GCRY_CIPHER_AES; #else case CIPHER_ALGO_AES: return 0; #endif #ifdef GPG_USE_AES192 case CIPHER_ALGO_AES192: return GCRY_CIPHER_AES192; #else case CIPHER_ALGO_AES192: return 0; #endif #ifdef GPG_USE_AES256 case CIPHER_ALGO_AES256: return GCRY_CIPHER_AES256; #else case CIPHER_ALGO_AES256: return 0; #endif #ifdef GPG_USE_TWOFISH case CIPHER_ALGO_TWOFISH: return GCRY_CIPHER_TWOFISH; #else case CIPHER_ALGO_TWOFISH: return 0; #endif #ifdef GPG_USE_CAMELLIA128 case CIPHER_ALGO_CAMELLIA128: return GCRY_CIPHER_CAMELLIA128; #else case CIPHER_ALGO_CAMELLIA128: return 0; #endif #ifdef GPG_USE_CAMELLIA192 case CIPHER_ALGO_CAMELLIA192: return GCRY_CIPHER_CAMELLIA192; #else case CIPHER_ALGO_CAMELLIA192: return 0; #endif #ifdef GPG_USE_CAMELLIA256 case CIPHER_ALGO_CAMELLIA256: return GCRY_CIPHER_CAMELLIA256; #else case CIPHER_ALGO_CAMELLIA256: return 0; #endif default: return 0; } } /* The inverse function of above. */ static cipher_algo_t map_cipher_gcry_to_openpgp (enum gcry_cipher_algos algo) { switch (algo) { case GCRY_CIPHER_NONE: return CIPHER_ALGO_NONE; case GCRY_CIPHER_IDEA: return CIPHER_ALGO_IDEA; case GCRY_CIPHER_3DES: return CIPHER_ALGO_3DES; case GCRY_CIPHER_CAST5: return CIPHER_ALGO_CAST5; case GCRY_CIPHER_BLOWFISH: return CIPHER_ALGO_BLOWFISH; case GCRY_CIPHER_AES: return CIPHER_ALGO_AES; case GCRY_CIPHER_AES192: return CIPHER_ALGO_AES192; case GCRY_CIPHER_AES256: return CIPHER_ALGO_AES256; case GCRY_CIPHER_TWOFISH: return CIPHER_ALGO_TWOFISH; case GCRY_CIPHER_CAMELLIA128: return CIPHER_ALGO_CAMELLIA128; case GCRY_CIPHER_CAMELLIA192: return CIPHER_ALGO_CAMELLIA192; case GCRY_CIPHER_CAMELLIA256: return CIPHER_ALGO_CAMELLIA256; default: return 0; } } /* Return the block length of an OpenPGP cipher algorithm. */ int openpgp_cipher_blocklen (cipher_algo_t algo) { /* We use the numbers from OpenPGP to be sure that we get the right block length. This is so that the packet parsing code works even for unknown algorithms (for which we assume 8 due to tradition). NOTE: If you change the returned blocklen above 16, check the callers because they may use a fixed size buffer of that size. */ switch (algo) { case CIPHER_ALGO_AES: case CIPHER_ALGO_AES192: case CIPHER_ALGO_AES256: case CIPHER_ALGO_TWOFISH: case CIPHER_ALGO_CAMELLIA128: case CIPHER_ALGO_CAMELLIA192: case CIPHER_ALGO_CAMELLIA256: return 16; default: return 8; } } /**************** * Wrapper around the libgcrypt function with additional checks on * the OpenPGP constraints for the algo ID. */ int openpgp_cipher_test_algo (cipher_algo_t algo) { enum gcry_cipher_algos ga; ga = map_cipher_openpgp_to_gcry (algo); if (!ga) return gpg_error (GPG_ERR_CIPHER_ALGO); return gcry_cipher_test_algo (ga); } /* Map the OpenPGP cipher algorithm whose ID is contained in ALGORITHM to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_cipher_algo_name (cipher_algo_t algo) { switch (algo) { case CIPHER_ALGO_IDEA: return "IDEA"; case CIPHER_ALGO_3DES: return "3DES"; case CIPHER_ALGO_CAST5: return "CAST5"; case CIPHER_ALGO_BLOWFISH: return "BLOWFISH"; case CIPHER_ALGO_AES: return "AES"; case CIPHER_ALGO_AES192: return "AES192"; case CIPHER_ALGO_AES256: return "AES256"; case CIPHER_ALGO_TWOFISH: return "TWOFISH"; case CIPHER_ALGO_CAMELLIA128: return "CAMELLIA128"; case CIPHER_ALGO_CAMELLIA192: return "CAMELLIA192"; case CIPHER_ALGO_CAMELLIA256: return "CAMELLIA256"; case CIPHER_ALGO_NONE: default: return "?"; } } /* Same as openpgp_cipher_algo_name but returns a string in the form * "ALGO.MODE". If AEAD is 0 "CFB" is used for the mode. */ const char * openpgp_cipher_algo_mode_name (cipher_algo_t algo, aead_algo_t aead) { return map_static_strings ("openpgp_cipher_algo_mode_name", algo, aead, openpgp_cipher_algo_name (algo), ".", aead? openpgp_aead_algo_name (aead) : "CFB", NULL); } /* Return 0 if ALGO is supported. Return an error if not. */ gpg_error_t openpgp_aead_test_algo (aead_algo_t algo) { /* FIXME: We currently have no easy way to test whether libgcrypt * implements a mode. The only way we can do this is to open a * cipher context with that mode and close it immediately. That is * a bit costly. Thus in case we add another algo we need to look * at the libgcrypt version and assume nothing has been patched out. */ switch (algo) { case AEAD_ALGO_NONE: break; case AEAD_ALGO_EAX: case AEAD_ALGO_OCB: return 0; } return gpg_error (GPG_ERR_INV_CIPHER_MODE); } /* Map the OpenPGP AEAD algorithm with ID ALGO to a string * representation of the algorithm name. For unknown algorithm IDs * this function returns "?". */ const char * openpgp_aead_algo_name (aead_algo_t algo) { switch (algo) { case AEAD_ALGO_NONE: break; case AEAD_ALGO_EAX: return "EAX"; case AEAD_ALGO_OCB: return "OCB"; } return "?"; } /* Return information for the AEAD algorithm ALGO. The corresponding * Libgcrypt ciphermode is stored at R_MODE and the required number of * octets for the nonce at R_NONCELEN. On error and error code is * returned. Note that the taglen is always 128 bits. */ gpg_error_t openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode, unsigned int *r_noncelen) { switch (algo) { case AEAD_ALGO_OCB: *r_mode = GCRY_CIPHER_MODE_OCB; *r_noncelen = 15; break; case AEAD_ALGO_EAX: *r_mode = MY_GCRY_CIPHER_MODE_EAX; *r_noncelen = 16; break; default: log_error ("unsupported AEAD algo %d\n", algo); return gpg_error (GPG_ERR_INV_CIPHER_MODE); } return 0; } /* Return 0 if ALGO is a supported OpenPGP public key algorithm. */ int openpgp_pk_test_algo (pubkey_algo_t algo) { return openpgp_pk_test_algo2 (algo, 0); } /* Return 0 if ALGO is a supported OpenPGP public key algorithm and allows the usage USE. */ int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use) { enum gcry_pk_algos ga = 0; size_t use_buf = use; switch (algo) { #ifdef GPG_USE_RSA case PUBKEY_ALGO_RSA: ga = GCRY_PK_RSA; break; case PUBKEY_ALGO_RSA_E: ga = GCRY_PK_RSA_E; break; case PUBKEY_ALGO_RSA_S: ga = GCRY_PK_RSA_S; break; #else case PUBKEY_ALGO_RSA: break; case PUBKEY_ALGO_RSA_E: break; case PUBKEY_ALGO_RSA_S: break; #endif case PUBKEY_ALGO_ELGAMAL_E: ga = GCRY_PK_ELG; break; case PUBKEY_ALGO_DSA: ga = GCRY_PK_DSA; break; #ifdef GPG_USE_ECDH case PUBKEY_ALGO_ECDH: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_ECDH: break; #endif #ifdef GPG_USE_ECDSA case PUBKEY_ALGO_ECDSA: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_ECDSA: break; #endif #ifdef GPG_USE_EDDSA case PUBKEY_ALGO_EDDSA: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_EDDSA: break; #endif case PUBKEY_ALGO_ELGAMAL: /* Don't allow type 20 keys unless in rfc2440 mode. */ if (RFC2440) ga = GCRY_PK_ELG; break; + case PUBKEY_ALGO_KYBER: ga = GCRY_PK_KEM; break; + default: break; } if (!ga) return gpg_error (GPG_ERR_PUBKEY_ALGO); /* Elgamal in OpenPGP used to support signing and Libgcrypt still * does. However, we removed the signing capability from gpg ages * ago. This function should reflect this so that errors are thrown * early and not only when we try to sign using Elgamal. */ if (ga == GCRY_PK_ELG && (use & (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG))) return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); /* Now check whether Libgcrypt has support for the algorithm. */ return gcry_pk_algo_info (ga, GCRYCTL_TEST_ALGO, NULL, &use_buf); } int openpgp_pk_algo_usage ( int algo ) { int use = 0; /* They are hardwired in gpg 1.0. */ switch ( algo ) { case PUBKEY_ALGO_RSA: use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC | PUBKEY_USAGE_AUTH); break; case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_ECDH: use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_RSA_S: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL: if (RFC2440) use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_ELGAMAL_E: use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_KYBER: use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_DIL3_25519: case PUBKEY_ALGO_DIL5_448: case PUBKEY_ALGO_SPHINX_SHA2: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; default: break; } return use; } /* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_pk_algo_name (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return "RSA"; case PUBKEY_ALGO_ELGAMAL: case PUBKEY_ALGO_ELGAMAL_E: return "ELG"; case PUBKEY_ALGO_DSA: return "DSA"; case PUBKEY_ALGO_ECDH: return "ECDH"; case PUBKEY_ALGO_ECDSA: return "ECDSA"; case PUBKEY_ALGO_EDDSA: return "EDDSA"; default: return "?"; } } /* Explicit mapping of OpenPGP digest algos to Libgcrypt. */ /* FIXME: We do not yes use it everywhere. */ enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo) { switch (algo) { #ifdef GPG_USE_MD5 case DIGEST_ALGO_MD5: return GCRY_MD_MD5; #else case DIGEST_ALGO_MD5: return 0; #endif case DIGEST_ALGO_SHA1: return GCRY_MD_SHA1; #ifdef GPG_USE_RMD160 case DIGEST_ALGO_RMD160: return GCRY_MD_RMD160; #else case DIGEST_ALGO_RMD160: return 0; #endif #ifdef GPG_USE_SHA224 case DIGEST_ALGO_SHA224: return GCRY_MD_SHA224; #else case DIGEST_ALGO_SHA224: return 0; #endif case DIGEST_ALGO_SHA256: return GCRY_MD_SHA256; #ifdef GPG_USE_SHA384 case DIGEST_ALGO_SHA384: return GCRY_MD_SHA384; #else case DIGEST_ALGO_SHA384: return 0; #endif #ifdef GPG_USE_SHA512 case DIGEST_ALGO_SHA512: return GCRY_MD_SHA512; #else case DIGEST_ALGO_SHA512: return 0; #endif default: return 0; } } /* Return 0 if ALGO is suitable and implemented OpenPGP hash algorithm. */ int openpgp_md_test_algo (digest_algo_t algo) { enum gcry_md_algos ga; ga = map_md_openpgp_to_gcry (algo); if (!ga) return gpg_error (GPG_ERR_DIGEST_ALGO); return gcry_md_test_algo (ga); } /* Map the OpenPGP digest algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_md_algo_name (int algo) { switch (algo) { case DIGEST_ALGO_MD5: return "MD5"; case DIGEST_ALGO_SHA1: return "SHA1"; case DIGEST_ALGO_RMD160: return "RIPEMD160"; case DIGEST_ALGO_SHA256: return "SHA256"; case DIGEST_ALGO_SHA384: return "SHA384"; case DIGEST_ALGO_SHA512: return "SHA512"; case DIGEST_ALGO_SHA224: return "SHA224"; } return "?"; } static unsigned long get_signature_count (PKT_public_key *pk) { #ifdef ENABLE_CARD_SUPPORT struct agent_card_info_s info; (void)pk; if (!agent_scd_getattr ("SIG-COUNTER",&info)) return info.sig_counter; else return 0; #else (void)pk; return 0; #endif } /* Expand %-strings. Returns a string which must be xfreed. Returns NULL if the string cannot be expanded (too large). */ char * pct_expando (ctrl_t ctrl, const char *string,struct expando_args *args) { const char *ch=string; int idx=0,maxlen=0,done=0; u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0}; char *ret=NULL; /* The parser below would return NULL for an empty string, thus we * catch it here. Also catch NULL here. */ if (!string || !*string) return xstrdup (""); if(args->pk) keyid_from_pk(args->pk,pk_keyid); if(args->pksk) keyid_from_pk (args->pksk, sk_keyid); /* This is used so that %k works in photoid command strings in --list-secret-keys (which of course has a sk, but no pk). */ if(!args->pk && args->pksk) keyid_from_pk (args->pksk, pk_keyid); while(*ch!='\0') { if(!done) { /* 8192 is way bigger than we'll need here */ if(maxlen>=8192) goto fail; maxlen+=1024; ret=xrealloc(ret,maxlen); } done=0; if(*ch=='%') { switch(*(ch+1)) { case 's': /* short key id */ if(idx+8namehash) { char *tmp = zb32_encode (args->namehash, 8*20); if (tmp) { if (idx + strlen (tmp) < maxlen) { strcpy (ret+idx, tmp); idx += strlen (tmp); } xfree (tmp); done = 1; } } break; case 'c': /* signature count from card, if any. */ if(idx+10pksk)); idx+=strlen(&ret[idx]); done=1; } break; case 'f': /* Fingerprint of key being signed */ case 'p': /* Fingerprint of the primary key making the signature. */ case 'g': /* Fingerprint of the key making the signature. */ { byte array[MAX_FINGERPRINT_LEN]; size_t len; int i; if ((*(ch+1))=='f' && args->pk) fingerprint_from_pk (args->pk, array, &len); else if ((*(ch+1))=='p' && args->pksk) { if(args->pksk->flags.primary) fingerprint_from_pk (args->pksk, array, &len); else if (args->pksk->main_keyid[0] || args->pksk->main_keyid[1]) { /* Not the primary key: Find the fingerprint of the primary key. */ PKT_public_key *pk= xmalloc_clear(sizeof(PKT_public_key)); if (!get_pubkey_fast (ctrl, pk,args->pksk->main_keyid)) fingerprint_from_pk (pk, array, &len); else memset (array, 0, (len=MAX_FINGERPRINT_LEN)); free_public_key (pk); } else /* Oops: info about the primary key missing. */ memset(array,0,(len=MAX_FINGERPRINT_LEN)); } else if((*(ch+1))=='g' && args->pksk) fingerprint_from_pk (args->pksk, array, &len); else memset(array,0,(len=MAX_FINGERPRINT_LEN)); if(idx+(len*2)validity_info && idx+1validity_info; ret[idx]='\0'; done=1; } break; /* The text string types */ case 't': case 'T': case 'V': { const char *str=NULL; switch(*(ch+1)) { case 't': /* e.g. "jpg" */ str=image_type_to_string(args->imagetype,0); break; case 'T': /* e.g. "image/jpeg" */ str=image_type_to_string(args->imagetype,2); break; case 'V': /* e.g. "full", "expired", etc. */ str=args->validity_string; break; } if(str && idx+strlen(str) 2) result = 0; } else result = 0; return result; } /* * Wrapper around gcry_md_map_name to provide a fallback using the * "Hn" syntax as used by the preference strings. */ int string_to_digest_algo (const char *string) { int val; /* FIXME: We should make use of our wrapper function and not assume that there is a 1 to 1 mapping between OpenPGP and Libgcrypt. */ val = gcry_md_map_name (string); if (!val && string && (string[0]=='H' || string[0]=='h')) { char *endptr; string++; val = strtol (string, &endptr, 10); if (!*string || *endptr || openpgp_md_test_algo (val)) val = 0; } return val; } const char * compress_algo_to_string(int algo) { const char *s=NULL; switch(algo) { case COMPRESS_ALGO_NONE: s=_("Uncompressed"); break; case COMPRESS_ALGO_ZIP: s="ZIP"; break; case COMPRESS_ALGO_ZLIB: s="ZLIB"; break; #ifdef HAVE_BZIP2 case COMPRESS_ALGO_BZIP2: s="BZIP2"; break; #endif } return s; } int string_to_compress_algo(const char *string) { /* TRANSLATORS: See doc/TRANSLATE about this string. */ if(match_multistr(_("uncompressed|none"),string)) return 0; else if(ascii_strcasecmp(string,"uncompressed")==0) return 0; else if(ascii_strcasecmp(string,"none")==0) return 0; else if(ascii_strcasecmp(string,"zip")==0) return 1; else if(ascii_strcasecmp(string,"zlib")==0) return 2; #ifdef HAVE_BZIP2 else if(ascii_strcasecmp(string,"bzip2")==0) return 3; #endif else if(ascii_strcasecmp(string,"z0")==0) return 0; else if(ascii_strcasecmp(string,"z1")==0) return 1; else if(ascii_strcasecmp(string,"z2")==0) return 2; #ifdef HAVE_BZIP2 else if(ascii_strcasecmp(string,"z3")==0) return 3; #endif else return -1; } int check_compress_algo(int algo) { switch (algo) { case 0: return 0; #ifdef HAVE_ZIP case 1: case 2: return 0; #endif #ifdef HAVE_BZIP2 case 3: return 0; #endif default: return GPG_ERR_COMPR_ALGO; } } int default_cipher_algo(void) { if(opt.def_cipher_algo) return opt.def_cipher_algo; else if(opt.personal_cipher_prefs) return opt.personal_cipher_prefs[0].value; else return opt.s2k_cipher_algo; } /* There is no default_digest_algo function, but see sign.c:hash_for() */ int default_compress_algo(void) { if(opt.compress_algo!=-1) return opt.compress_algo; else if(opt.personal_compress_prefs) return opt.personal_compress_prefs[0].value; else return DEFAULT_COMPRESS_ALGO; } void compliance_failure(void) { char *ver="???"; switch(opt.compliance) { case CO_GNUPG: ver="GnuPG"; break; case CO_RFC4880: ver="OpenPGP"; break; case CO_RFC2440: ver="OpenPGP (older)"; break; case CO_PGP7: ver="PGP 7.x"; break; case CO_PGP8: ver="PGP 8.x"; break; case CO_DE_VS: ver="DE-VS applications"; break; } log_info(_("this message may not be usable by %s\n"),ver); opt.compliance=CO_GNUPG; } /* Break a string into successive option pieces. Accepts single word options and key=value argument options. */ char * optsep(char **stringp) { char *tok,*end; tok=*stringp; if(tok) { end=strpbrk(tok," ,="); if(end) { int sawequals=0; char *ptr=end; /* what we need to do now is scan along starting with *end, If the next character we see (ignoring spaces) is an = sign, then there is an argument. */ while(*ptr) { if(*ptr=='=') sawequals=1; else if(*ptr!=' ') break; ptr++; } /* There is an argument, so grab that too. At this point, ptr points to the first character of the argument. */ if(sawequals) { /* Is it a quoted argument? */ if(*ptr=='"') { ptr++; end=strchr(ptr,'"'); if(end) end++; } else end=strpbrk(ptr," ,"); } if(end && *end) { *end='\0'; *stringp=end+1; } else *stringp=NULL; } else *stringp=NULL; } return tok; } /* Breaks an option value into key and value. Returns NULL if there is no value. Note that "string" is modified to remove the =value part. */ char * argsplit(char *string) { char *equals,*arg=NULL; equals=strchr(string,'='); if(equals) { char *quote,*space; *equals='\0'; arg=equals+1; /* Quoted arg? */ quote=strchr(arg,'"'); if(quote) { arg=quote+1; quote=strchr(arg,'"'); if(quote) *quote='\0'; } else { size_t spaces; /* Trim leading spaces off of the arg */ spaces=strspn(arg," "); arg+=spaces; } /* Trim tailing spaces off of the tag */ space=strchr(string,' '); if(space) *space='\0'; } return arg; } /* Return the length of the initial token, leaving off any argument. */ static size_t optlen(const char *s) { char *end=strpbrk(s," ="); if(end) return end-s; else return strlen(s); } /* Note: This function returns true on success. */ int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy) { char *tok; if (str && (!strcmp (str, "help") || !strcmp (str, "full-help"))) { int i,maxlen=0; int full = *str == 'f'; /* Figure out the longest option name so we can line these up neatly. */ for(i=0;opts[i].name;i++) if(opts[i].help && maxlen='A' && file[0]<='Z') || (file[0]>='a' && file[0]<='z')) && file[1]==':') #else || file[0]=='/' #endif ) return access(file,mode); else { /* At least as large as, but most often larger than we need. */ char *buffer=xmalloc(strlen(envpath)+1+strlen(file)+1); char *split,*item,*path=xstrdup(envpath); split=path; while((item=strsep(&split,PATHSEP_S))) { strcpy(buffer,item); strcat(buffer,"/"); strcat(buffer,file); ret=access(buffer,mode); if(ret==0) break; } xfree(path); xfree(buffer); } return ret; } /* Return the number of public key parameters as used by OpenPGP. */ int pubkey_get_npkey (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 2; case PUBKEY_ALGO_ELGAMAL_E: return 3; case PUBKEY_ALGO_DSA: return 4; case PUBKEY_ALGO_ECDH: return 3; case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 3; case PUBKEY_ALGO_EDDSA: return 2; case PUBKEY_ALGO_KYBER: return 3; default: return 0; } } /* Return the number of secret key parameters as used by OpenPGP. */ int pubkey_get_nskey (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 6; case PUBKEY_ALGO_ELGAMAL_E: return 4; case PUBKEY_ALGO_DSA: return 5; case PUBKEY_ALGO_ECDH: return 4; case PUBKEY_ALGO_ECDSA: return 3; case PUBKEY_ALGO_ELGAMAL: return 4; case PUBKEY_ALGO_EDDSA: return 3; case PUBKEY_ALGO_KYBER: return 5; default: return 0; } } /* Temporary helper. */ int pubkey_get_nsig (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 1; case PUBKEY_ALGO_ELGAMAL_E: return 0; case PUBKEY_ALGO_DSA: return 2; case PUBKEY_ALGO_ECDH: return 0; case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 2; default: return 0; } } /* Temporary helper. */ int pubkey_get_nenc (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 1; case PUBKEY_ALGO_ELGAMAL_E: return 2; case PUBKEY_ALGO_DSA: return 0; case PUBKEY_ALGO_ECDH: return 2; case PUBKEY_ALGO_ECDSA: return 0; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 0; case PUBKEY_ALGO_KYBER: return 4; default: return 0; } } /* Temporary helper. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *key ) { int rc, nbits; gcry_sexp_t sexp; if (algo == PUBKEY_ALGO_DSA && key[0] && key[1] && key[2] && key[3]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", key[0], key[1], key[2], key[3] ); } else if ((algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) && key[0] && key[1] && key[2]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", key[0], key[1], key[2] ); } else if (is_RSA (algo) && key[0] && key[1]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(rsa(n%m)(e%m)))", key[0], key[1] ); } else if ((algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_EDDSA) && key[0] && key[1]) { char *curve = openpgp_oid_to_str (key[0]); if (!curve) rc = gpg_error_from_syserror (); else { rc = gcry_sexp_build (&sexp, NULL, "(public-key(ecc(curve%s)(q%m)))", curve, key[1]); xfree (curve); } } else return 0; if (rc) BUG (); nbits = gcry_pk_get_nbits (sexp); gcry_sexp_release (sexp); return nbits; } int mpi_print (estream_t fp, gcry_mpi_t a, int mode) { int n = 0; size_t nwritten; if (!a) return es_fprintf (fp, "[MPI_NULL]"); if (!mode) { unsigned int n1; n1 = gcry_mpi_get_nbits(a); n += es_fprintf (fp, "[%u bits]", n1); } else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; unsigned char *p = gcry_mpi_get_opaque (a, &nbits); if (!p) n += es_fprintf (fp, "[invalid opaque value]"); else { if (!es_write_hexstring (fp, p, (nbits + 7)/8, 0, &nwritten)) n += nwritten; } } else { unsigned char *buffer; size_t buflen; if (gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &buflen, a)) BUG (); if (!es_write_hexstring (fp, buffer, buflen, 0, &nwritten)) n += nwritten; gcry_free (buffer); } return n; } /* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point, i.e. 04 */ unsigned int ecdsa_qbits_from_Q (unsigned int qbits) { if ((qbits%8) > 3) { log_error (_("ECDSA public key is expected to be in SEC encoding " "multiple of 8 bits\n")); return 0; } qbits -= qbits%8; qbits /= 2; return qbits; } /* Ignore signatures and certifications made over certain digest * algorithms by default, MD5 is considered weak. This allows users * to deprecate support for other algorithms as well. */ void additional_weak_digest (const char* digestname) { struct weakhash *weak = NULL; const enum gcry_md_algos algo = string_to_digest_algo(digestname); if (algo == GCRY_MD_NONE) { log_error (_("unknown weak digest '%s'\n"), digestname); return; } /* Check to ensure it's not already present. */ for (weak = opt.weak_digests; weak; weak = weak->next) if (algo == weak->algo) return; /* Add it to the head of the list. */ weak = xmalloc(sizeof(*weak)); weak->algo = algo; weak->rejection_shown = 0; weak->next = opt.weak_digests; opt.weak_digests = weak; } /* Return true if ALGO is in the list of weak digests. */ int is_weak_digest (digest_algo_t algo) { const enum gcry_md_algos galgo = map_md_openpgp_to_gcry (algo); const struct weakhash *weak; for (weak = opt.weak_digests; weak; weak = weak->next) if (weak->algo == galgo) return 1; return 0; } diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 2163787cb..c55bb1b71 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1,3950 +1,3956 @@ /* parse-packet.c - read packets * Copyright (C) 1998-2007, 2009-2010 Free Software Foundation, Inc. * Copyright (C) 2014, 2018 Werner Koch * Copyright (C) 2015 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0+ */ #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "packet.h" #include "../common/iobuf.h" #include "filter.h" #include "photoid.h" #include "options.h" #include "main.h" #include "../common/i18n.h" #include "../common/host2net.h" #include "../common/mbox-util.h" static int mpi_print_mode; static int list_mode; static estream_t listfp; /* A linked list of known notation names. Note that the FLAG is used * to store the length of the name to speed up the check. */ static strlist_t known_notations_list; static int parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, int *skip, IOBUF out, int do_skip #if DEBUG_PARSE_PACKET , const char *dbg_w, const char *dbg_f, int dbg_l #endif ); static int copy_packet (IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen, int partial); static void skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial); static void *read_rest (IOBUF inp, size_t pktlen); static int parse_marker (IOBUF inp, int pkttype, unsigned long pktlen); static int parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig * ops); static int parse_key (IOBUF inp, int pkttype, unsigned long pktlen, byte * hdr, int hdrlen, PACKET * packet); static int parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static int parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet); static gpg_error_t parse_ring_trust (parse_packet_ctx_t ctx, unsigned long pktlen); static int parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb, int partial); static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb); static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb, int partial); static gpg_error_t parse_encrypted_aead (IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet, int partial); static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb); static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int partial); /* Read a 16-bit value in MSB order (big endian) from an iobuf. */ static unsigned short read_16 (IOBUF inp) { unsigned short a; a = (unsigned short)iobuf_get_noeof (inp) << 8; a |= iobuf_get_noeof (inp); return a; } /* Read a 32-bit value in MSB order (big endian) from an iobuf. */ static unsigned long read_32 (IOBUF inp) { unsigned long a; a = (unsigned long)iobuf_get_noeof (inp) << 24; a |= iobuf_get_noeof (inp) << 16; a |= iobuf_get_noeof (inp) << 8; a |= iobuf_get_noeof (inp); return a; } /* Read an external representation of an MPI and return the MPI. The external format is a 16-bit unsigned value stored in network byte order giving the number of bits for the following integer. The integer is stored MSB first and is left padded with zero bits to align on a byte boundary. The caller must set *RET_NREAD to the maximum number of bytes to read from the pipeline INP. This function sets *RET_NREAD to be the number of bytes actually read from the pipeline. If SECURE is true, the integer is stored in secure memory (allocated using gcry_xmalloc_secure). */ static gcry_mpi_t mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure) { int c, c1, c2, i; unsigned int nmax = *ret_nread; unsigned int nbits, nbytes; size_t nread = 0; gcry_mpi_t a = NULL; byte *buf = NULL; byte *p; if (!nmax) goto overflow; if ((c = c1 = iobuf_get (inp)) == -1) goto leave; if (++nread == nmax) goto overflow; nbits = c << 8; if ((c = c2 = iobuf_get (inp)) == -1) goto leave; ++nread; nbits |= c; if (nbits > MAX_EXTERN_MPI_BITS) { log_error ("mpi too large (%u bits)\n", nbits); goto leave; } nbytes = (nbits + 7) / 8; buf = secure ? gcry_xmalloc_secure (nbytes + 2) : gcry_xmalloc (nbytes + 2); p = buf; p[0] = c1; p[1] = c2; for (i = 0; i < nbytes; i++) { if (nread == nmax) goto overflow; c = iobuf_get (inp); if (c == -1) goto leave; p[i + 2] = c; nread ++; } if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread)) a = NULL; *ret_nread = nread; gcry_free(buf); return a; overflow: log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax); leave: *ret_nread = nread; gcry_free(buf); return a; } /* If NLENGTH is zero read an octet string of length NBYTES from INP * and return it at R_DATA. * * If NLENGTH is either 1, 2, or 4 and NLENGTH is zero read an * NLENGTH-octet count and use this count number octets from INP and * return it at R_DATA. * * On error return an error code and store NULL at R_DATA. PKTLEN * shall give the current length of the packet and is updated with * each read. If SECURE is true, the integer is stored in secure * memory (allocated using gcry_xmalloc_secure). */ static gpg_error_t read_octet_string (iobuf_t inp, unsigned long *pktlen, unsigned int nlength, unsigned int nbytes, int secure, gcry_mpi_t *r_data) { gpg_error_t err; int c, i; byte *buf = NULL; byte *p; *r_data = NULL; if ((nbytes && nlength) || (!nbytes && !(nlength == 1 || nlength == 2 || nlength == 4))) { err = gpg_error (GPG_ERR_INV_ARG); goto leave; } if (nlength) { for (i = 0; i < nlength; i++) { if (!*pktlen) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } c = iobuf_readbyte (inp); if (c < 0) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } --*pktlen; nbytes <<= 8; nbytes |= c; } if (!nbytes) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } } if (nbytes*8 > (nbytes==4? MAX_EXTERN_KEYPARM_BITS:MAX_EXTERN_MPI_BITS) || (nbytes*8 < nbytes)) { log_error ("octet string too large (%u octets)\n", nbytes); err = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } if (nbytes > *pktlen) { log_error ("octet string larger than packet (%u octets)\n", nbytes); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } buf = secure ? gcry_malloc_secure (nbytes) : gcry_malloc (nbytes); if (!buf) { err = gpg_error_from_syserror (); goto leave; } p = buf; for (i = 0; i < nbytes; i++) { c = iobuf_get (inp); if (c == -1) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } p[i] = c; --*pktlen; } *r_data = gcry_mpi_set_opaque (NULL, buf, nbytes*8); gcry_mpi_set_flag (*r_data, GCRYMPI_FLAG_USER2); return 0; leave: gcry_free (buf); return err; } /* Read an external representation of an SOS and return the opaque MPI with GCRYMPI_FLAG_USER2. The external format is a 16-bit unsigned value stored in network byte order giving information for the following octets. The caller must set *RET_NREAD to the maximum number of bytes to read from the pipeline INP. This function sets *RET_NREAD to be the number of bytes actually read from the pipeline. If SECURE is true, the integer is stored in secure memory (allocated using gcry_xmalloc_secure). */ static gcry_mpi_t sos_read (iobuf_t inp, unsigned int *ret_nread, int secure) { int c, c1, c2, i; unsigned int nmax = *ret_nread; unsigned int nbits, nbytes; size_t nread = 0; gcry_mpi_t a = NULL; byte *buf = NULL; byte *p; if (!nmax) goto overflow; if ((c = c1 = iobuf_get (inp)) == -1) goto leave; if (++nread == nmax) goto overflow; nbits = c << 8; if ((c = c2 = iobuf_get (inp)) == -1) goto leave; ++nread; nbits |= c; if (nbits > MAX_EXTERN_MPI_BITS) { log_error ("mpi too large (%u bits)\n", nbits); goto leave; } nbytes = (nbits + 7) / 8; buf = secure ? gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes); p = buf; for (i = 0; i < nbytes; i++) { if (nread == nmax) goto overflow; c = iobuf_get (inp); if (c == -1) goto leave; p[i] = c; nread ++; } a = gcry_mpi_set_opaque (NULL, buf, nbits); gcry_mpi_set_flag (a, GCRYMPI_FLAG_USER2); *ret_nread = nread; return a; overflow: log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax); leave: *ret_nread = nread; gcry_free(buf); return a; } /* Register STRING as a known critical notation name. */ void register_known_notation (const char *string) { strlist_t sl; if (!known_notations_list) { sl = add_to_strlist (&known_notations_list, "preferred-email-encoding@pgp.com"); sl->flags = 32; /* Length of the string. */ } if (!string) return; /* Only initialized the default known notations. */ /* In --set-notation we use an exclamation mark to indicate a * critical notation. As a convenience skip this here. */ if (*string == '!') string++; if (!*string || strlist_find (known_notations_list, string)) return; /* Empty string or already registered. */ sl = add_to_strlist (&known_notations_list, string); sl->flags = strlen (string); } int set_packet_list_mode (int mode) { int old = list_mode; list_mode = mode; /* We use stdout only if invoked by the --list-packets command but switch to stderr in all other cases. This breaks the previous behaviour but that seems to be more of a bug than intentional. I don't believe that any application makes use of this long standing annoying way of printing to stdout except when doing a --list-packets. If this assumption fails, it will be easy to add an option for the listing stream. Note that we initialize it only once; mainly because there is code which switches opt.list_mode back to 1 and we want to have all output to the same stream. The MPI_PRINT_MODE will be enabled if the corresponding debug flag is set or if we are in --list-packets and --verbose is given. Using stderr is not actually very clean because it bypasses the logging code but it is a special thing anyway. I am not sure whether using log_stream() would be better. Perhaps we should enable the list mode only with a special option. */ if (!listfp) { if (opt.list_packets) { listfp = es_stdout; if (opt.verbose) mpi_print_mode = 1; } else listfp = es_stderr; if (DBG_MPI) mpi_print_mode = 1; } return old; } /* If OPT.VERBOSE is set, print a warning that the algorithm ALGO is not suitable for signing and encryption. */ static void unknown_pubkey_warning (int algo) { static byte unknown_pubkey_algos[256]; /* First check whether the algorithm is usable but not suitable for encryption/signing. */ if (pubkey_get_npkey (algo)) { if (opt.verbose && !glo_ctrl.silence_parse_warnings) { if (!pubkey_get_nsig (algo)) log_info ("public key algorithm %s not suitable for %s\n", openpgp_pk_algo_name (algo), "signing"); if (!pubkey_get_nenc (algo)) log_info ("public key algorithm %s not suitable for %s\n", openpgp_pk_algo_name (algo), "encryption"); } } else { algo &= 0xff; if (!unknown_pubkey_algos[algo]) { if (opt.verbose && !glo_ctrl.silence_parse_warnings) log_info (_("can't handle public key algorithm %d\n"), algo); unknown_pubkey_algos[algo] = 1; } } } #if DEBUG_PARSE_PACKET int dbg_parse_packet (parse_packet_ctx_t ctx, PACKET *pkt, const char *dbg_f, int dbg_l) { int skip, rc; do { rc = parse (ctx, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l); } while (skip && ! rc); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int parse_packet (parse_packet_ctx_t ctx, PACKET *pkt) { int skip, rc; do { rc = parse (ctx, pkt, 0, NULL, &skip, NULL, 0); } while (skip && ! rc); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Like parse packet, but only return secret or public (sub)key * packets. */ #if DEBUG_PARSE_PACKET int dbg_search_packet (parse_packet_ctx_t ctx, PACKET *pkt, off_t * retpos, int with_uid, const char *dbg_f, int dbg_l) { int skip, rc; do { rc = parse (ctx, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search", dbg_f, dbg_l); } while (skip && ! rc); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int search_packet (parse_packet_ctx_t ctx, PACKET *pkt, off_t * retpos, int with_uid) { int skip, rc; do { rc = parse (ctx, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0); } while (skip && ! rc); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Copy all packets from INP to OUT, thereby removing unused spaces. */ #if DEBUG_PARSE_PACKET int dbg_copy_all_packets (iobuf_t inp, iobuf_t out, const char *dbg_f, int dbg_l) { PACKET pkt; struct parse_packet_ctx_s parsectx; int skip, rc = 0; if (! out) log_bug ("copy_all_packets: OUT may not be NULL.\n"); init_parse_packet (&parsectx, inp); do { init_packet (&pkt); } while (! (rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l))); deinit_parse_packet (&parsectx); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int copy_all_packets (iobuf_t inp, iobuf_t out) { PACKET pkt; struct parse_packet_ctx_s parsectx; int skip, rc = 0; if (! out) log_bug ("copy_all_packets: OUT may not be NULL.\n"); init_parse_packet (&parsectx, inp); do { init_packet (&pkt); } while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0))); deinit_parse_packet (&parsectx); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Copy some packets from INP to OUT, thereby removing unused spaces. * Stop at offset STOPoff (i.e. don't copy packets at this or later * offsets) */ #if DEBUG_PARSE_PACKET int dbg_copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff, const char *dbg_f, int dbg_l) { int rc = 0; PACKET pkt; int skip; struct parse_packet_ctx_s parsectx; init_parse_packet (&parsectx, inp); do { if (iobuf_tell (inp) >= stopoff) { deinit_parse_packet (&parsectx); return 0; } init_packet (&pkt); } while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0, "some", dbg_f, dbg_l))); deinit_parse_packet (&parsectx); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff) { int rc = 0; PACKET pkt; struct parse_packet_ctx_s parsectx; int skip; init_parse_packet (&parsectx, inp); do { if (iobuf_tell (inp) >= stopoff) { deinit_parse_packet (&parsectx); return 0; } init_packet (&pkt); } while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0))); deinit_parse_packet (&parsectx); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* * Skip over N packets */ #if DEBUG_PARSE_PACKET int dbg_skip_some_packets (iobuf_t inp, unsigned n, const char *dbg_f, int dbg_l) { int rc = 0; int skip; PACKET pkt; struct parse_packet_ctx_s parsectx; init_parse_packet (&parsectx, inp); for (; n && !rc; n--) { init_packet (&pkt); rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l); } deinit_parse_packet (&parsectx); return rc; } #else /*!DEBUG_PARSE_PACKET*/ int skip_some_packets (iobuf_t inp, unsigned int n) { int rc = 0; int skip; PACKET pkt; struct parse_packet_ctx_s parsectx; init_parse_packet (&parsectx, inp); for (; n && !rc; n--) { init_packet (&pkt); rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1); } deinit_parse_packet (&parsectx); return rc; } #endif /*!DEBUG_PARSE_PACKET*/ /* Parse a packet and save it in *PKT. If OUT is not NULL and the packet is valid (its type is not 0), then the header, the initial length field and the packet's contents are written to OUT. In this case, the packet is not saved in *PKT. ONLYKEYPKTS is a simple packet filter. If ONLYKEYPKTS is set to 1, then only public subkey packets, public key packets, private subkey packets and private key packets are parsed. The rest are skipped (i.e., the header and the contents are read from the pipeline and discarded). If ONLYKEYPKTS is set to 2, then in addition to the above 4 types of packets, user id packets are also accepted. DO_SKIP is a more coarse grained filter. Unless ONLYKEYPKTS is set to 2 and the packet is a user id packet, all packets are skipped. Finally, if a packet is invalid (it's type is 0), it is skipped. If a packet is skipped and SKIP is not NULL, then *SKIP is set to 1. Note: ONLYKEYPKTS and DO_SKIP are only respected if OUT is NULL, i.e., the packets are not simply being copied. If RETPOS is not NULL, then the position of CTX->INP (as returned by iobuf_tell) is saved there before any data is read from CTX->INP. */ static int parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, int *skip, IOBUF out, int do_skip #if DEBUG_PARSE_PACKET , const char *dbg_w, const char *dbg_f, int dbg_l #endif ) { int rc = 0; iobuf_t inp; int c, ctb, pkttype, lenbytes; unsigned long pktlen; byte hdr[8]; int hdrlen; int new_ctb = 0, partial = 0; int with_uid = (onlykeypkts == 2); off_t pos; *skip = 0; inp = ctx->inp; again: log_assert (!pkt->pkt.generic); if (retpos || list_mode) { pos = iobuf_tell (inp); if (retpos) *retpos = pos; } else pos = 0; /* (silence compiler warning) */ /* The first byte of a packet is the so-called tag. The highest bit must be set. */ if ((ctb = iobuf_get (inp)) == -1) { rc = -1; goto leave; } hdrlen = 0; hdr[hdrlen++] = ctb; if (!(ctb & 0x80)) { log_error ("%s: invalid packet (ctb=%02x)\n", iobuf_where (inp), ctb); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } /* Immediately following the header is the length. There are two formats: the old format and the new format. If bit 6 (where the least significant bit is bit 0) is set in the tag, then we are dealing with a new format packet. Otherwise, it is an old format packet. */ pktlen = 0; new_ctb = !!(ctb & 0x40); if (new_ctb) { /* Get the packet's type. This is encoded in the 6 least significant bits of the tag. */ pkttype = ctb & 0x3f; /* Extract the packet's length. New format packets have 4 ways to encode the packet length. The value of the first byte determines the encoding and partially determines the length. See section 4.2.2 of RFC 4880 for details. */ if ((c = iobuf_get (inp)) == -1) { log_error ("%s: 1st length byte missing\n", iobuf_where (inp)); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } hdr[hdrlen++] = c; if (c < 192) pktlen = c; else if (c < 224) { pktlen = (c - 192) * 256; if ((c = iobuf_get (inp)) == -1) { log_error ("%s: 2nd length byte missing\n", iobuf_where (inp)); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } hdr[hdrlen++] = c; pktlen += c + 192; } else if (c == 255) { int i; char value[4]; for (i = 0; i < 4; i ++) { if ((c = iobuf_get (inp)) == -1) { log_error ("%s: 4 byte length invalid\n", iobuf_where (inp)); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } value[i] = hdr[hdrlen++] = c; } pktlen = buf32_to_ulong (value); } else /* Partial body length. */ { switch (pkttype) { case PKT_PLAINTEXT: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_AEAD: case PKT_COMPRESSED: iobuf_set_partial_body_length_mode (inp, c & 0xff); pktlen = 0; /* To indicate partial length. */ partial = 1; break; default: log_error ("%s: partial length invalid for" " packet type %d\n", iobuf_where (inp), pkttype); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } } } else /* This is an old format packet. */ { /* Extract the packet's type. This is encoded in bits 2-5. */ pkttype = (ctb >> 2) & 0xf; /* The type of length encoding is encoded in bits 0-1 of the tag. */ lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3)); if (!lenbytes) { pktlen = 0; /* Don't know the value. */ /* This isn't really partial, but we can treat it the same in a "read until the end" sort of way. */ partial = 1; if (pkttype != PKT_ENCRYPTED && pkttype != PKT_PLAINTEXT && pkttype != PKT_COMPRESSED) { log_error ("%s: indeterminate length for invalid" " packet type %d\n", iobuf_where (inp), pkttype); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } } else { for (; lenbytes; lenbytes--) { pktlen <<= 8; c = iobuf_get (inp); if (c == -1) { log_error ("%s: length invalid\n", iobuf_where (inp)); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } pktlen |= hdr[hdrlen++] = c; } } } /* Sometimes the decompressing layer enters an error state in which it simply outputs 0xff for every byte read. If we have a stream of 0xff bytes, then it will be detected as a new format packet with type 63 and a 4-byte encoded length that is 4G-1. Since packets with type 63 are private and we use them as a control packet, which won't be 4 GB, we reject such packets as invalid. */ if (pkttype == 63 && pktlen == 0xFFFFFFFF) { /* With some probability this is caused by a problem in the * the uncompressing layer - in some error cases it just loops * and spits out 0xff bytes. */ log_error ("%s: garbled packet detected\n", iobuf_where (inp)); g10_exit (2); } if (out && pkttype) { /* This type of copying won't work if the packet uses a partial body length. (In other words, this only works if HDR is actually the length.) Currently, no callers require this functionality so we just log this as an error. */ if (partial) { log_error ("parse: Can't copy partial packet. Aborting.\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } rc = iobuf_write (out, hdr, hdrlen); if (!rc) rc = copy_packet (inp, out, pkttype, pktlen, partial); goto leave; } if (with_uid && pkttype == PKT_USER_ID) /* If ONLYKEYPKTS is set to 2, then we never skip user id packets, even if DO_SKIP is set. */ ; else if (do_skip /* type==0 is not allowed. This is an invalid packet. */ || !pkttype /* When ONLYKEYPKTS is set, we don't skip keys. */ || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY && pkttype != PKT_PUBLIC_KEY && pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY)) { iobuf_skip_rest (inp, pktlen, partial); *skip = 1; rc = 0; goto leave; } if (DBG_PACKET) { #if DEBUG_PARSE_PACKET log_debug ("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n", iobuf_id (inp), pkttype, pktlen, new_ctb ? " (new_ctb)" : "", dbg_w, dbg_f, dbg_l); #else log_debug ("parse_packet(iob=%d): type=%d length=%lu%s\n", iobuf_id (inp), pkttype, pktlen, new_ctb ? " (new_ctb)" : ""); #endif } if (list_mode) es_fprintf (listfp, "# off=%lu ctb=%02x tag=%d hlen=%d plen=%lu%s%s\n", (unsigned long)pos, ctb, pkttype, hdrlen, pktlen, partial? (new_ctb ? " partial" : " indeterminate") :"", new_ctb? " new-ctb":""); /* Count it. */ ctx->n_parsed_packets++; pkt->pkttype = pkttype; rc = GPG_ERR_UNKNOWN_PACKET; /* default error */ switch (pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: pkt->pkt.public_key = xmalloc_clear (sizeof *pkt->pkt.public_key); rc = parse_key (inp, pkttype, pktlen, hdr, hdrlen, pkt); break; case PKT_SYMKEY_ENC: rc = parse_symkeyenc (inp, pkttype, pktlen, pkt); break; case PKT_PUBKEY_ENC: rc = parse_pubkeyenc (inp, pkttype, pktlen, pkt); break; case PKT_SIGNATURE: pkt->pkt.signature = xmalloc_clear (sizeof *pkt->pkt.signature); rc = parse_signature (inp, pkttype, pktlen, pkt->pkt.signature); break; case PKT_ONEPASS_SIG: pkt->pkt.onepass_sig = xmalloc_clear (sizeof *pkt->pkt.onepass_sig); rc = parse_onepass_sig (inp, pkttype, pktlen, pkt->pkt.onepass_sig); break; case PKT_USER_ID: rc = parse_user_id (inp, pkttype, pktlen, pkt); break; case PKT_ATTRIBUTE: pkt->pkttype = pkttype = PKT_USER_ID; /* we store it in the userID */ rc = parse_attribute (inp, pkttype, pktlen, pkt); break; case PKT_OLD_COMMENT: case PKT_COMMENT: rc = parse_comment (inp, pkttype, pktlen, pkt); break; case PKT_RING_TRUST: { rc = parse_ring_trust (ctx, pktlen); if (!rc) goto again; /* Directly read the next packet. */ } break; case PKT_PLAINTEXT: rc = parse_plaintext (inp, pkttype, pktlen, pkt, new_ctb, partial); break; case PKT_COMPRESSED: rc = parse_compressed (inp, pkttype, pktlen, pkt, new_ctb); break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: rc = parse_encrypted (inp, pkttype, pktlen, pkt, new_ctb, partial); break; case PKT_MDC: rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb); break; case PKT_ENCRYPTED_AEAD: rc = parse_encrypted_aead (inp, pkttype, pktlen, pkt, partial); break; case PKT_GPG_CONTROL: rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial); break; case PKT_MARKER: rc = parse_marker (inp, pkttype, pktlen); break; default: /* Unknown packet. Skip it. */ skip_packet (inp, pkttype, pktlen, partial); break; } /* Store a shallow copy of certain packets in the context. */ free_packet (NULL, ctx); if (!rc && (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY || pkttype == PKT_USER_ID || pkttype == PKT_ATTRIBUTE || pkttype == PKT_SIGNATURE)) { ctx->last_pkt = *pkt; } leave: /* FIXME: We leak in case of an error (see the xmalloc's above). */ if (!rc && iobuf_error (inp)) rc = GPG_ERR_INV_KEYRING; /* FIXME: We use only the error code for now to avoid problems with callers which have not been checked to always use gpg_err_code() when comparing error codes. */ return rc == -1? -1 : gpg_err_code (rc); } static void dump_hex_line (int c, int *i) { if (*i && !(*i % 8)) { if (*i && !(*i % 24)) es_fprintf (listfp, "\n%4d:", *i); else es_putc (' ', listfp); } if (c == -1) es_fprintf (listfp, " EOF"); else es_fprintf (listfp, " %02x", c); ++*i; } /* Copy the contents of a packet from the pipeline IN to the pipeline OUT. The header and length have already been read from INP and the decoded values are given as PKGTYPE and PKTLEN. If the packet is a partial body length packet (RFC 4880, Section 4.2.2.4), then iobuf_set_partial_block_modeiobuf_set_partial_block_mode should already have been called on INP and PARTIAL should be set. If PARTIAL is set or PKTLEN is 0 and PKTTYPE is PKT_COMPRESSED, copy until the first EOF is encountered on INP. Returns 0 on success and an error code if an error occurs. */ static int copy_packet (IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen, int partial) { int rc; int n; char buf[100]; if (partial) { while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1) if ((rc = iobuf_write (out, buf, n))) return rc; /* write error */ } else if (!pktlen && pkttype == PKT_COMPRESSED) { log_debug ("copy_packet: compressed!\n"); /* compressed packet, copy till EOF */ while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1) if ((rc = iobuf_write (out, buf, n))) return rc; /* write error */ } else { for (; pktlen; pktlen -= n) { n = pktlen > sizeof (buf) ? sizeof (buf) : pktlen; n = iobuf_read (inp, buf, n); if (n == -1) return gpg_error (GPG_ERR_EOF); if ((rc = iobuf_write (out, buf, n))) return rc; /* write error */ } } return 0; } /* Skip an unknown packet. PKTTYPE is the packet's type, PKTLEN is the length of the packet's content and PARTIAL is whether partial body length encoding in used (in this case PKTLEN is ignored). */ static void skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial) { if (list_mode) { es_fprintf (listfp, ":unknown packet: type %2d, length %lu\n", pkttype, pktlen); if (pkttype) { int c, i = 0; es_fputs ("dump:", listfp); if (partial) { while ((c = iobuf_get (inp)) != -1) dump_hex_line (c, &i); } else { for (; pktlen; pktlen--) { dump_hex_line ((c = iobuf_get (inp)), &i); if (c == -1) break; } } es_putc ('\n', listfp); return; } } iobuf_skip_rest (inp, pktlen, partial); } /* Read PKTLEN bytes from INP and return them in a newly allocated * buffer. In case of an error (including reading fewer than PKTLEN * bytes from INP before EOF is returned), NULL is returned and an * error message is logged. */ static void * read_rest (IOBUF inp, size_t pktlen) { int c; byte *buf, *p; buf = xtrymalloc (pktlen); if (!buf) { gpg_error_t err = gpg_error_from_syserror (); log_error ("error reading rest of packet: %s\n", gpg_strerror (err)); return NULL; } for (p = buf; pktlen; pktlen--) { c = iobuf_get (inp); if (c == -1) { log_error ("premature eof while reading rest of packet\n"); xfree (buf); return NULL; } *p++ = c; } return buf; } /* Read a special size+body from INP. On success store an opaque MPI * with it at R_DATA. The caller shall store the remaining size of * the packet at PKTLEN. On error return an error code and store NULL * at R_DATA. Even in the error case store the number of read bytes * at PKTLEN is updated. */ static gpg_error_t read_sized_octet_string (iobuf_t inp, unsigned long *pktlen, gcry_mpi_t *r_data) { char buffer[256]; char *tmpbuf; int i, c, nbytes; *r_data = NULL; if (!*pktlen) return gpg_error (GPG_ERR_INV_PACKET); c = iobuf_readbyte (inp); if (c < 0) return gpg_error (GPG_ERR_INV_PACKET); --*pktlen; nbytes = c; if (nbytes < 2 || nbytes > 254) return gpg_error (GPG_ERR_INV_PACKET); if (nbytes > *pktlen) return gpg_error (GPG_ERR_INV_PACKET); buffer[0] = nbytes; for (i = 0; i < nbytes; i++) { c = iobuf_get (inp); if (c < 0) return gpg_error (GPG_ERR_INV_PACKET); --*pktlen; buffer[1+i] = c; } tmpbuf = xtrymalloc (1 + nbytes); if (!tmpbuf) return gpg_error_from_syserror (); memcpy (tmpbuf, buffer, 1 + nbytes); *r_data = gcry_mpi_set_opaque (NULL, tmpbuf, 8 * (1 + nbytes)); if (!*r_data) { xfree (tmpbuf); return gpg_error_from_syserror (); } return 0; } /* Parse a marker packet. */ static int parse_marker (IOBUF inp, int pkttype, unsigned long pktlen) { (void) pkttype; if (pktlen != 3) goto fail; if (iobuf_get (inp) != 'P') { pktlen--; goto fail; } if (iobuf_get (inp) != 'G') { pktlen--; goto fail; } if (iobuf_get (inp) != 'P') { pktlen--; goto fail; } if (list_mode) es_fputs (":marker packet: PGP\n", listfp); return 0; fail: log_error ("invalid marker packet\n"); if (list_mode) es_fputs (":marker packet: [invalid]\n", listfp); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } static int parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { PKT_symkey_enc *k; int rc = 0; int i, version, s2kmode, cipher_algo, aead_algo, hash_algo, seskeylen, minlen; if (pktlen < 4) goto too_short; version = iobuf_get_noeof (inp); pktlen--; if (version == 4) ; else if (version == 5) ; else { log_error ("packet(%d) with unknown version %d\n", pkttype, version); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [unknown version]\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if (pktlen > 200) { /* (we encode the seskeylen in a byte) */ log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [too large]\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } cipher_algo = iobuf_get_noeof (inp); pktlen--; if (version == 5) { aead_algo = iobuf_get_noeof (inp); pktlen--; } else aead_algo = 0; if (pktlen < 2) goto too_short; s2kmode = iobuf_get_noeof (inp); pktlen--; hash_algo = iobuf_get_noeof (inp); pktlen--; switch (s2kmode) { case 0: /* Simple S2K. */ minlen = 0; break; case 1: /* Salted S2K. */ minlen = 8; break; case 3: /* Iterated+salted S2K. */ minlen = 9; break; default: log_error ("unknown S2K mode %d\n", s2kmode); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [unknown S2K mode]\n"); goto leave; } if (minlen > pktlen) { log_error ("packet with S2K %d too short\n", s2kmode); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [too short]\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } seskeylen = pktlen - minlen; k = packet->pkt.symkey_enc = xmalloc_clear (sizeof *packet->pkt.symkey_enc + seskeylen - 1); k->version = version; k->cipher_algo = cipher_algo; k->aead_algo = aead_algo; k->s2k.mode = s2kmode; k->s2k.hash_algo = hash_algo; if (s2kmode == 1 || s2kmode == 3) { for (i = 0; i < 8 && pktlen; i++, pktlen--) k->s2k.salt[i] = iobuf_get_noeof (inp); } if (s2kmode == 3) { k->s2k.count = iobuf_get_noeof (inp); pktlen--; } k->seskeylen = seskeylen; if (k->seskeylen) { for (i = 0; i < seskeylen && pktlen; i++, pktlen--) k->seskey[i] = iobuf_get_noeof (inp); /* What we're watching out for here is a session key decryptor with no salt. The RFC says that using salt for this is a MUST. */ if (s2kmode != 1 && s2kmode != 3) log_info (_("WARNING: potentially insecure symmetrically" " encrypted session key\n")); } log_assert (!pktlen); if (list_mode) { es_fprintf (listfp, ":symkey enc packet: version %d, cipher %d, aead %d," " s2k %d, hash %d", version, cipher_algo, aead_algo, s2kmode, hash_algo); if (seskeylen) { /* To compute the size of the session key we need to know * the size of the AEAD nonce which we may not know. Thus * we show only the size of the entire encrypted session * key. */ if (aead_algo) es_fprintf (listfp, ", encrypted seskey %d bytes", seskeylen); else es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8); } es_fprintf (listfp, "\n"); if (s2kmode == 1 || s2kmode == 3) { es_fprintf (listfp, "\tsalt "); es_write_hexstring (listfp, k->s2k.salt, 8, 0, NULL); if (s2kmode == 3) es_fprintf (listfp, ", count %lu (%lu)", S2K_DECODE_COUNT ((ulong) k->s2k.count), (ulong) k->s2k.count); es_fprintf (listfp, "\n"); } } leave: iobuf_skip_rest (inp, pktlen, 0); return rc; too_short: log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fprintf (listfp, ":symkey enc packet: [too short]\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } +/* Parse a public key encrypted packet (Tag 1). */ static int parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { int rc = 0; int i, ndata; unsigned int n; PKT_pubkey_enc *k; k = packet->pkt.pubkey_enc = xmalloc_clear (sizeof *packet->pkt.pubkey_enc); if (pktlen < 12) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":pubkey enc packet: [too short]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } k->version = iobuf_get_noeof (inp); pktlen--; if (k->version != 2 && k->version != 3) { log_error ("packet(%d) with unknown version %d\n", pkttype, k->version); if (list_mode) es_fputs (":pubkey enc packet: [unknown version]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } k->keyid[0] = read_32 (inp); pktlen -= 4; k->keyid[1] = read_32 (inp); pktlen -= 4; k->pubkey_algo = iobuf_get_noeof (inp); pktlen--; k->throw_keyid = 0; /* Only used as flag for build_packet. */ if (list_mode) es_fprintf (listfp, ":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n", k->version, k->pubkey_algo, (ulong) k->keyid[0], (ulong) k->keyid[1]); ndata = pubkey_get_nenc (k->pubkey_algo); if (!ndata) { if (list_mode) es_fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo); unknown_pubkey_warning (k->pubkey_algo); k->data[0] = NULL; /* No need to store the encrypted data. */ } else if (k->pubkey_algo == PUBKEY_ALGO_ECDH) { log_assert (ndata == 2); /* Get the ephemeral public key. */ n = pktlen; k->data[0] = sos_read (inp, &n, 0); pktlen -= n; if (!k->data[0]) { rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } /* Get the wrapped symmetric key. */ rc = read_sized_octet_string (inp, &pktlen, k->data + 1); if (rc) goto leave; } else if (k->pubkey_algo == PUBKEY_ALGO_KYBER) { log_assert (ndata == 4); /* Get the ephemeral public key. */ - rc = read_octet_string (inp, &pktlen, 4, 0, 0, k->data + 0); - if (rc) - goto leave; + n = pktlen; + k->data[0] = sos_read (inp, &n, 0); + pktlen -= n; + if (!k->data[0]) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } /* Get the Kyber ciphertext. */ rc = read_octet_string (inp, &pktlen, 4, 0, 0, k->data + 1); if (rc) goto leave; /* Get the algorithm id. */ rc = read_octet_string (inp, &pktlen, 0, 1, 0, k->data + 2); if (rc) goto leave; /* Get the wrapped symmetric key. */ rc = read_sized_octet_string (inp, &pktlen, k->data + 3); if (rc) goto leave; } else { for (i = 0; i < ndata; i++) { n = pktlen; k->data[i] = mpi_read (inp, &n, 0); pktlen -= n; if (!k->data[i]) rc = gpg_error (GPG_ERR_INV_PACKET); } if (rc) goto leave; } if (list_mode) { for (i = 0; i < ndata; i++) { es_fprintf (listfp, "\tdata: "); mpi_print (listfp, k->data[i], mpi_print_mode); es_putc ('\n', listfp); } } leave: iobuf_skip_rest (inp, pktlen, 0); return rc; } /* Dump a subpacket to LISTFP. BUFFER contains the subpacket in * question and points to the type field in the subpacket header (not * the start of the header). TYPE is the subpacket's type with the * critical bit cleared. CRITICAL is the value of the CRITICAL bit. * BUFLEN is the length of the buffer and LENGTH is the length of the * subpacket according to the subpacket's header. DIGEST_ALGO is the * digest algo of the signature. */ static void dump_sig_subpkt (int hashed, int type, int critical, const byte * buffer, size_t buflen, size_t length, int digest_algo) { const char *p = NULL; int i; int nprinted; /* The CERT has warning out with explains how to use GNUPG to detect * the ARRs - we print our old message here when it is a faked ARR * and add an additional notice. */ if (type == SIGSUBPKT_ARR && !hashed) { es_fprintf (listfp, "\tsubpkt %d len %u (additional recipient request)\n" "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically " "encrypt to this key and thereby reveal the plaintext to " "the owner of this ARR key. Detailed info follows:\n", type, (unsigned) length); } buffer++; length--; nprinted = es_fprintf (listfp, "\t%s%ssubpkt %d len %u (", /*) */ critical ? "critical " : "", hashed ? "hashed " : "", type, (unsigned) length); if (nprinted < 1) nprinted = 1; /*(we use (nprinted-1) later.)*/ if (length > buflen) { es_fprintf (listfp, "too short: buffer is only %u)\n", (unsigned) buflen); return; } switch (type) { case SIGSUBPKT_SIG_CREATED: if (length >= 4) es_fprintf (listfp, "sig created %s", strtimestamp (buf32_to_u32 (buffer))); break; case SIGSUBPKT_SIG_EXPIRE: if (length >= 4) { if (buf32_to_u32 (buffer)) es_fprintf (listfp, "sig expires after %s", strtimevalue (buf32_to_u32 (buffer))); else es_fprintf (listfp, "sig does not expire"); } break; case SIGSUBPKT_EXPORTABLE: if (length) es_fprintf (listfp, "%sexportable", *buffer ? "" : "not "); break; case SIGSUBPKT_TRUST: if (length != 2) p = "[invalid trust subpacket]"; else es_fprintf (listfp, "trust signature of depth %d, value %d", buffer[0], buffer[1]); break; case SIGSUBPKT_REGEXP: if (!length) p = "[invalid regexp subpacket]"; else { es_fprintf (listfp, "regular expression: \""); es_write_sanitized (listfp, buffer, length, "\"", NULL); p = "\""; } break; case SIGSUBPKT_REVOCABLE: if (length) es_fprintf (listfp, "%srevocable", *buffer ? "" : "not "); break; case SIGSUBPKT_KEY_EXPIRE: if (length >= 4) { if (buf32_to_u32 (buffer)) es_fprintf (listfp, "key expires after %s", strtimevalue (buf32_to_u32 (buffer))); else es_fprintf (listfp, "key does not expire"); } break; case SIGSUBPKT_PREF_SYM: es_fputs ("pref-sym-algos:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %d", buffer[i]); break; case SIGSUBPKT_PREF_AEAD: es_fputs ("pref-aead-algos:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %d", buffer[i]); break; case SIGSUBPKT_REV_KEY: es_fputs ("revocation key: ", listfp); if (length < 22) p = "[too short]"; else { es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]); for (i = 2; i < length; i++) es_fprintf (listfp, "%02X", buffer[i]); } break; case SIGSUBPKT_ISSUER: if (length >= 8) es_fprintf (listfp, "issuer key ID %08lX%08lX", (ulong) buf32_to_u32 (buffer), (ulong) buf32_to_u32 (buffer + 4)); break; case SIGSUBPKT_ISSUER_FPR: if (length >= 21) { char *tmp; es_fprintf (listfp, "issuer fpr v%d ", buffer[0]); tmp = bin2hex (buffer+1, length-1, NULL); if (tmp) { es_fputs (tmp, listfp); xfree (tmp); } } break; case SIGSUBPKT_NOTATION: { es_fputs ("notation: ", listfp); if (length < 8) p = "[too short]"; else { const byte *s = buffer; size_t n1, n2; n1 = (s[4] << 8) | s[5]; n2 = (s[6] << 8) | s[7]; s += 8; if (8 + n1 + n2 != length) p = "[error]"; else { es_write_sanitized (listfp, s, n1, ")", NULL); es_putc ('=', listfp); if (*buffer & 0x80) es_write_sanitized (listfp, s + n1, n2, ")", NULL); else p = "[not human readable]"; } } } break; case SIGSUBPKT_PREF_HASH: es_fputs ("pref-hash-algos:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %d", buffer[i]); break; case SIGSUBPKT_PREF_COMPR: es_fputs ("pref-zip-algos:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %d", buffer[i]); break; case SIGSUBPKT_KS_FLAGS: es_fputs ("keyserver preferences:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %02X", buffer[i]); break; case SIGSUBPKT_PREF_KS: es_fputs ("preferred keyserver: ", listfp); es_write_sanitized (listfp, buffer, length, ")", NULL); break; case SIGSUBPKT_PRIMARY_UID: p = "primary user ID"; break; case SIGSUBPKT_POLICY: es_fputs ("policy: ", listfp); es_write_sanitized (listfp, buffer, length, ")", NULL); break; case SIGSUBPKT_KEY_FLAGS: es_fputs ("key flags:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %02X", buffer[i]); break; case SIGSUBPKT_SIGNERS_UID: p = "signer's user ID"; break; case SIGSUBPKT_REVOC_REASON: if (length) { es_fprintf (listfp, "revocation reason 0x%02x (", *buffer); es_write_sanitized (listfp, buffer + 1, length - 1, ")", NULL); p = ")"; } break; case SIGSUBPKT_ARR: es_fputs ("Big Brother's key (ignored): ", listfp); if (length < 22) p = "[too short]"; else { es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]); if (length > 2) es_write_hexstring (listfp, buffer+2, length-2, 0, NULL); } break; case SIGSUBPKT_FEATURES: es_fputs ("features:", listfp); for (i = 0; i < length; i++) es_fprintf (listfp, " %02x", buffer[i]); break; case SIGSUBPKT_SIGNATURE: es_fputs ("signature: ", listfp); if (length < 17) p = "[too short]"; else es_fprintf (listfp, "v%d, class 0x%02X, algo %d, digest algo %d", buffer[0], buffer[0] == 3 ? buffer[2] : buffer[1], buffer[0] == 3 ? buffer[15] : buffer[2], buffer[0] == 3 ? buffer[16] : buffer[3]); break; case SIGSUBPKT_ATTST_SIGS: { unsigned int hlen; es_fputs ("attst-sigs: ", listfp); hlen = gcry_md_get_algo_dlen (map_md_openpgp_to_gcry (digest_algo)); if (!hlen) p = "[unknown digest algo]"; else if ((length % hlen)) p = "[invalid length]"; else { es_fprintf (listfp, "%u", (unsigned int)length/hlen); while (length) { es_fprintf (listfp, "\n\t%*s", nprinted-1, ""); es_write_hexstring (listfp, buffer, hlen, 0, NULL); buffer += hlen; length -= hlen; } } } break; case SIGSUBPKT_KEY_BLOCK: es_fputs ("key-block: ", listfp); if (length && buffer[0]) p = "[unknown reserved octet]"; else if (length < 50) /* 50 is an arbitrary min. length. */ p = "[invalid subpacket]"; else { /* estream_t fp; */ /* fp = es_fopen ("a.key-block", "wb"); */ /* log_assert (fp); */ /* es_fwrite ( buffer+1, length-1, 1, fp); */ /* es_fclose (fp); */ es_fprintf (listfp, "[%u octets]", (unsigned int)length-1); } break; default: if (type >= 100 && type <= 110) p = "experimental / private subpacket"; else p = "?"; break; } es_fprintf (listfp, "%s)\n", p ? p : ""); } /* * Returns: >= 0 use this offset into buffer * -1 explicitly reject returning this type * -2 subpacket too short */ int parse_one_sig_subpkt (const byte * buffer, size_t n, int type) { switch (type) { case SIGSUBPKT_REV_KEY: if (n < 22) break; return 0; case SIGSUBPKT_SIG_CREATED: case SIGSUBPKT_SIG_EXPIRE: case SIGSUBPKT_KEY_EXPIRE: if (n < 4) break; return 0; case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_KS_FLAGS: case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_AEAD: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_POLICY: case SIGSUBPKT_PREF_KS: case SIGSUBPKT_FEATURES: case SIGSUBPKT_REGEXP: case SIGSUBPKT_ATTST_SIGS: return 0; case SIGSUBPKT_SIGNATURE: case SIGSUBPKT_EXPORTABLE: case SIGSUBPKT_REVOCABLE: case SIGSUBPKT_REVOC_REASON: if (!n) break; return 0; case SIGSUBPKT_ISSUER: /* issuer key ID */ if (n < 8) break; return 0; case SIGSUBPKT_ISSUER_FPR: /* issuer key fingerprint */ if (n < 21) break; return 0; case SIGSUBPKT_NOTATION: /* minimum length needed, and the subpacket must be well-formed where the name length and value length all fit inside the packet. */ if (n < 8 || 8 + ((buffer[4] << 8) | buffer[5]) + ((buffer[6] << 8) | buffer[7]) != n) break; return 0; case SIGSUBPKT_PRIMARY_UID: if (n != 1) break; return 0; case SIGSUBPKT_TRUST: if (n != 2) break; return 0; case SIGSUBPKT_KEY_BLOCK: if (n && buffer[0]) return -1; /* Unknown version - ignore. */ if (n < 50) break; /* Definitely too short to carry a key block. */ return 0; default: return 0; } return -2; } /* Return true if we understand the critical notation. */ static int can_handle_critical_notation (const byte *name, size_t len) { strlist_t sl; register_known_notation (NULL); /* Make sure it is initialized. */ for (sl = known_notations_list; sl; sl = sl->next) if (sl->flags == len && !memcmp (sl->d, name, len)) return 1; /* Known */ if (opt.verbose && !glo_ctrl.silence_parse_warnings) { log_info(_("Unknown critical signature notation: ") ); print_utf8_buffer (log_get_stream(), name, len); log_printf ("\n"); } return 0; /* Unknown. */ } static int can_handle_critical (const byte * buffer, size_t n, int type) { switch (type) { case SIGSUBPKT_NOTATION: if (n >= 8) { size_t notation_len = ((buffer[4] << 8) | buffer[5]); if (n - 8 >= notation_len) return can_handle_critical_notation (buffer + 8, notation_len); } return 0; case SIGSUBPKT_SIGNATURE: case SIGSUBPKT_SIG_CREATED: case SIGSUBPKT_SIG_EXPIRE: case SIGSUBPKT_KEY_EXPIRE: case SIGSUBPKT_EXPORTABLE: case SIGSUBPKT_REVOCABLE: case SIGSUBPKT_REV_KEY: case SIGSUBPKT_ISSUER: /* issuer key ID */ case SIGSUBPKT_ISSUER_FPR: /* issuer fingerprint */ case SIGSUBPKT_PREF_SYM: case SIGSUBPKT_PREF_AEAD: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_PRIMARY_UID: case SIGSUBPKT_FEATURES: case SIGSUBPKT_TRUST: case SIGSUBPKT_REGEXP: case SIGSUBPKT_ATTST_SIGS: /* Is it enough to show the policy or keyserver? */ case SIGSUBPKT_POLICY: case SIGSUBPKT_PREF_KS: case SIGSUBPKT_REVOC_REASON: /* At least we know about it. */ return 1; case SIGSUBPKT_KEY_BLOCK: if (n && !buffer[0]) return 1; else return 0; default: return 0; } } const byte * enum_sig_subpkt (PKT_signature *sig, int want_hashed, sigsubpkttype_t reqtype, size_t *ret_n, int *start, int *critical) { const byte *buffer; int buflen; int type; int critical_dummy; int offset; size_t n; const subpktarea_t *pktbuf = want_hashed? sig->hashed : sig->unhashed; int seq = 0; int reqseq = start ? *start : 0; if (!critical) critical = &critical_dummy; if (!pktbuf || reqseq == -1) { static char dummy[] = "x"; /* Return a value different from NULL to indicate that * there is no critical bit we do not understand. */ return reqtype == SIGSUBPKT_TEST_CRITICAL ? dummy : NULL; } buffer = pktbuf->data; buflen = pktbuf->len; while (buflen) { n = *buffer++; buflen--; if (n == 255) /* 4 byte length header. */ { if (buflen < 4) goto too_short; n = buf32_to_size_t (buffer); buffer += 4; buflen -= 4; } else if (n >= 192) /* 4 byte special encoded length header. */ { if (buflen < 2) goto too_short; n = ((n - 192) << 8) + *buffer + 192; buffer++; buflen--; } if (buflen < n) goto too_short; if (!buflen) goto no_type_byte; type = *buffer; if (type & 0x80) { type &= 0x7f; *critical = 1; } else *critical = 0; if (!(++seq > reqseq)) ; else if (reqtype == SIGSUBPKT_TEST_CRITICAL) { if (*critical) { if (n - 1 > buflen + 1) goto too_short; if (!can_handle_critical (buffer + 1, n - 1, type)) { if (opt.verbose && !glo_ctrl.silence_parse_warnings) log_info (_("subpacket of type %d has " "critical bit set\n"), type); if (start) *start = seq; return NULL; /* This is an error. */ } } } else if (reqtype < 0) /* List packets. */ dump_sig_subpkt (reqtype == SIGSUBPKT_LIST_HASHED, type, *critical, buffer, buflen, n, sig->digest_algo); else if (type == reqtype) /* Found. */ { buffer++; n--; if (n > buflen) goto too_short; if (ret_n) *ret_n = n; offset = parse_one_sig_subpkt (buffer, n, type); switch (offset) { case -2: log_error ("subpacket of type %d too short\n", type); return NULL; case -1: return NULL; default: break; } if (start) *start = seq; return buffer + offset; } buffer += n; buflen -= n; } if (reqtype == SIGSUBPKT_TEST_CRITICAL) /* Returning NULL means we found a subpacket with the critical bit set that we don't grok. We've iterated over all the subpackets and haven't found such a packet so we need to return a non-NULL value. */ return buffer; /* Critical bit we don't understand. */ if (start) *start = -1; return NULL; /* End of packets; not found. */ too_short: if (opt.debug && !glo_ctrl.silence_parse_warnings) { es_fflush (es_stdout); log_printhex (pktbuf->data, pktbuf->len > 16? 16 : pktbuf->len, "buffer shorter than subpacket (%zu/%d/%zu); dump:", pktbuf->len, buflen, n); } if (start) *start = -1; return NULL; no_type_byte: if (opt.verbose && !glo_ctrl.silence_parse_warnings) log_info ("type octet missing in subpacket\n"); if (start) *start = -1; return NULL; } const byte * parse_sig_subpkt (PKT_signature *sig, int want_hashed, sigsubpkttype_t reqtype, size_t *ret_n) { return enum_sig_subpkt (sig, want_hashed, reqtype, ret_n, NULL, NULL); } const byte * parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype) { const byte *p; p = parse_sig_subpkt (sig, 1, reqtype, NULL); if (!p) p = parse_sig_subpkt (sig, 0, reqtype, NULL); return p; } /* Find all revocation keys. Look in hashed area only. */ void parse_revkeys (PKT_signature * sig) { const byte *revkey; int seq = 0; size_t len; if (sig->sig_class != 0x1F) return; while ((revkey = enum_sig_subpkt (sig, 1, SIGSUBPKT_REV_KEY, &len, &seq, NULL))) { /* Consider only valid packets. They must have a length of * either 2+20 or 2+32 octets and bit 7 of the class octet must * be set. */ if ((len == 22 || len == 34) && (revkey[0] & 0x80)) { sig->revkey = xrealloc (sig->revkey, sizeof (struct revocation_key) * (sig->numrevkeys + 1)); sig->revkey[sig->numrevkeys].class = revkey[0]; sig->revkey[sig->numrevkeys].algid = revkey[1]; len -= 2; sig->revkey[sig->numrevkeys].fprlen = len; memcpy (sig->revkey[sig->numrevkeys].fpr, revkey+2, len); memset (sig->revkey[sig->numrevkeys].fpr+len, 0, sizeof (sig->revkey[sig->numrevkeys].fpr) - len); sig->numrevkeys++; } } } int parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, PKT_signature * sig) { int md5_len = 0; unsigned n; int is_v4or5 = 0; int rc = 0; int i, ndata; if (pktlen < 16) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":signature packet: [too short]\n", listfp); goto leave; } sig->version = iobuf_get_noeof (inp); pktlen--; if (sig->version == 4 || sig->version == 5) is_v4or5 = 1; else if (sig->version != 2 && sig->version != 3) { log_error ("packet(%d) with unknown version %d\n", pkttype, sig->version); if (list_mode) es_fputs (":signature packet: [unknown version]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if (!is_v4or5) { if (pktlen == 0) goto underflow; md5_len = iobuf_get_noeof (inp); pktlen--; } if (pktlen == 0) goto underflow; sig->sig_class = iobuf_get_noeof (inp); pktlen--; if (!is_v4or5) { if (pktlen < 12) goto underflow; sig->timestamp = read_32 (inp); pktlen -= 4; sig->keyid[0] = read_32 (inp); pktlen -= 4; sig->keyid[1] = read_32 (inp); pktlen -= 4; } if (pktlen < 2) goto underflow; sig->pubkey_algo = iobuf_get_noeof (inp); pktlen--; sig->digest_algo = iobuf_get_noeof (inp); pktlen--; sig->flags.exportable = 1; sig->flags.revocable = 1; if (is_v4or5) /* Read subpackets. */ { if (pktlen < 2) goto underflow; n = read_16 (inp); pktlen -= 2; /* Length of hashed data. */ if (pktlen < n) goto underflow; if (n > 10000) { log_error ("signature packet: hashed data too long\n"); if (list_mode) es_fputs (":signature packet: [hashed data too long]\n", listfp); rc = GPG_ERR_INV_PACKET; goto leave; } if (n) { sig->hashed = xmalloc (sizeof (*sig->hashed) + n - 1); sig->hashed->size = n; sig->hashed->len = n; if (iobuf_read (inp, sig->hashed->data, n) != n) { log_error ("premature eof while reading " "hashed signature data\n"); if (list_mode) es_fputs (":signature packet: [premature eof]\n", listfp); rc = -1; goto leave; } pktlen -= n; } if (pktlen < 2) goto underflow; n = read_16 (inp); pktlen -= 2; /* Length of unhashed data. */ if (pktlen < n) goto underflow; if (n > 10000) { log_error ("signature packet: unhashed data too long\n"); if (list_mode) es_fputs (":signature packet: [unhashed data too long]\n", listfp); rc = GPG_ERR_INV_PACKET; goto leave; } if (n) { sig->unhashed = xmalloc (sizeof (*sig->unhashed) + n - 1); sig->unhashed->size = n; sig->unhashed->len = n; if (iobuf_read (inp, sig->unhashed->data, n) != n) { log_error ("premature eof while reading " "unhashed signature data\n"); if (list_mode) es_fputs (":signature packet: [premature eof]\n", listfp); rc = -1; goto leave; } pktlen -= n; } } if (pktlen < 2) goto underflow; sig->digest_start[0] = iobuf_get_noeof (inp); pktlen--; sig->digest_start[1] = iobuf_get_noeof (inp); pktlen--; if (is_v4or5 && sig->pubkey_algo) /* Extract required information. */ { const byte *p; size_t len; /* Set sig->flags.unknown_critical if there is a critical bit * set for packets which we do not understand. */ if (!parse_sig_subpkt (sig, 1, SIGSUBPKT_TEST_CRITICAL, NULL) || !parse_sig_subpkt (sig, 0, SIGSUBPKT_TEST_CRITICAL, NULL)) sig->flags.unknown_critical = 1; p = parse_sig_subpkt (sig, 1, SIGSUBPKT_SIG_CREATED, NULL); if (p) sig->timestamp = buf32_to_u32 (p); else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110) && opt.verbose > 1 && !glo_ctrl.silence_parse_warnings) log_info ("signature packet without timestamp\n"); /* Set the key id. We first try the issuer fingerprint and if * it is a v4 signature the fallback to the issuer. Note that * only the issuer packet is also searched in the unhashed area. */ p = parse_sig_subpkt (sig, 1, SIGSUBPKT_ISSUER_FPR, &len); if (p && len == 21 && p[0] == 4) { sig->keyid[0] = buf32_to_u32 (p + 1 + 12); sig->keyid[1] = buf32_to_u32 (p + 1 + 16); } else if (p && len == 33 && p[0] == 5) { sig->keyid[0] = buf32_to_u32 (p + 1 ); sig->keyid[1] = buf32_to_u32 (p + 1 + 4); } else if ((p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER))) { sig->keyid[0] = buf32_to_u32 (p); sig->keyid[1] = buf32_to_u32 (p + 4); } else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110) && opt.verbose > 1 && !glo_ctrl.silence_parse_warnings) log_info ("signature packet without keyid\n"); p = parse_sig_subpkt (sig, 1, SIGSUBPKT_SIG_EXPIRE, NULL); if (p && buf32_to_u32 (p)) sig->expiredate = sig->timestamp + buf32_to_u32 (p); if (sig->expiredate && sig->expiredate <= make_timestamp ()) sig->flags.expired = 1; p = parse_sig_subpkt (sig, 1, SIGSUBPKT_POLICY, NULL); if (p) sig->flags.policy_url = 1; p = parse_sig_subpkt (sig, 1, SIGSUBPKT_PREF_KS, NULL); if (p) sig->flags.pref_ks = 1; p = parse_sig_subpkt (sig, 1, SIGSUBPKT_SIGNERS_UID, &len); if (p && len) { char *mbox; sig->signers_uid = try_make_printable_string (p, len, 0); if (!sig->signers_uid) { rc = gpg_error_from_syserror (); goto leave; } mbox = mailbox_from_userid (sig->signers_uid, 0); if (mbox) { xfree (sig->signers_uid); sig->signers_uid = mbox; } } p = parse_sig_subpkt (sig, 1, SIGSUBPKT_KEY_BLOCK, NULL); if (p) sig->flags.key_block = 1; p = parse_sig_subpkt (sig, 1, SIGSUBPKT_NOTATION, NULL); if (p) sig->flags.notation = 1; p = parse_sig_subpkt (sig, 1, SIGSUBPKT_REVOCABLE, NULL); if (p && *p == 0) sig->flags.revocable = 0; p = parse_sig_subpkt (sig, 1, SIGSUBPKT_TRUST, &len); if (p && len == 2) { sig->trust_depth = p[0]; sig->trust_value = p[1]; /* Only look for a regexp if there is also a trust subpacket. */ sig->trust_regexp = parse_sig_subpkt (sig, 1, SIGSUBPKT_REGEXP, &len); /* If the regular expression is of 0 length, there is no regular expression. */ if (len == 0) sig->trust_regexp = NULL; } /* We accept the exportable subpacket from either the hashed or unhashed areas as older versions of gpg put it in the unhashed area. In theory, anyway, we should never see this packet off of a local keyring. */ p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE); if (p && *p == 0) sig->flags.exportable = 0; /* Find all revocation keys. */ if (sig->sig_class == 0x1F) parse_revkeys (sig); } if (list_mode) { es_fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n" "\tversion %d, created %lu, md5len %d, sigclass 0x%02x\n" "\tdigest algo %d, begin of digest %02x %02x\n", sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], sig->version, (ulong) sig->timestamp, md5_len, sig->sig_class, sig->digest_algo, sig->digest_start[0], sig->digest_start[1]); if (is_v4or5) { parse_sig_subpkt (sig, 1, SIGSUBPKT_LIST_HASHED, NULL); parse_sig_subpkt (sig, 0, SIGSUBPKT_LIST_UNHASHED, NULL); } } ndata = pubkey_get_nsig (sig->pubkey_algo); if (!ndata) { if (list_mode) es_fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo); unknown_pubkey_warning (sig->pubkey_algo); /* We store the plain material in data[0], so that we are able * to write it back with build_packet(). */ if (pktlen > (5 * MAX_EXTERN_MPI_BITS / 8)) { /* We include a limit to avoid too trivial DoS attacks by having gpg allocate too much memory. */ log_error ("signature packet: too much data\n"); rc = GPG_ERR_INV_PACKET; } else { void *tmpp; tmpp = read_rest (inp, pktlen); sig->data[0] = gcry_mpi_set_opaque (NULL, tmpp, tmpp? pktlen * 8 : 0); pktlen = 0; } } else { for (i = 0; i < ndata; i++) { n = pktlen; if (sig->pubkey_algo == PUBKEY_ALGO_ECDSA || sig->pubkey_algo == PUBKEY_ALGO_EDDSA) sig->data[i] = sos_read (inp, &n, 0); else sig->data[i] = mpi_read (inp, &n, 0); pktlen -= n; if (list_mode) { es_fprintf (listfp, "\tdata: "); mpi_print (listfp, sig->data[i], mpi_print_mode); es_putc ('\n', listfp); } if (!sig->data[i]) rc = GPG_ERR_INV_PACKET; } } leave: iobuf_skip_rest (inp, pktlen, 0); return rc; underflow: log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":signature packet: [too short]\n", listfp); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } static int parse_onepass_sig (IOBUF inp, int pkttype, unsigned long pktlen, PKT_onepass_sig * ops) { int version; int rc = 0; if (pktlen < 13) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":onepass_sig packet: [too short]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } version = iobuf_get_noeof (inp); pktlen--; if (version != 3) { log_error ("onepass_sig with unknown version %d\n", version); if (list_mode) es_fputs (":onepass_sig packet: [unknown version]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ops->sig_class = iobuf_get_noeof (inp); pktlen--; ops->digest_algo = iobuf_get_noeof (inp); pktlen--; ops->pubkey_algo = iobuf_get_noeof (inp); pktlen--; ops->keyid[0] = read_32 (inp); pktlen -= 4; ops->keyid[1] = read_32 (inp); pktlen -= 4; ops->last = iobuf_get_noeof (inp); pktlen--; if (list_mode) es_fprintf (listfp, ":onepass_sig packet: keyid %08lX%08lX\n" "\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, " "last=%d\n", (ulong) ops->keyid[0], (ulong) ops->keyid[1], version, ops->sig_class, ops->digest_algo, ops->pubkey_algo, ops->last); leave: iobuf_skip_rest (inp, pktlen, 0); return rc; } static int parse_key (IOBUF inp, int pkttype, unsigned long pktlen, byte * hdr, int hdrlen, PACKET * pkt) { gpg_error_t err = 0; int i, version, algorithm; unsigned long timestamp, expiredate, max_expiredate; int npkey, nskey; u32 keyid[2]; PKT_public_key *pk; int is_v5; unsigned int pkbytes; /* For v5 keys: Number of bytes in the public * key material. For v4 keys: 0. */ (void) hdr; pk = pkt->pkt.public_key; /* PK has been cleared. */ version = iobuf_get_noeof (inp); pktlen--; if (pkttype == PKT_PUBLIC_SUBKEY && version == '#') { /* Early versions of G10 used the old PGP comments packets; * luckily all those comments are started by a hash. */ if (list_mode) { es_fprintf (listfp, ":rfc1991 comment packet: \""); for (; pktlen; pktlen--) { int c; c = iobuf_get (inp); if (c == -1) break; /* Ooops: shorter than indicated. */ if (c >= ' ' && c <= 'z') es_putc (c, listfp); else es_fprintf (listfp, "\\x%02x", c); } es_fprintf (listfp, "\"\n"); } iobuf_skip_rest (inp, pktlen, 0); return 0; } else if (version == 4) is_v5 = 0; else if (version == 5) is_v5 = 1; else if (version == 2 || version == 3) { /* Not anymore supported since 2.1. Use an older gpg version * (i.e. gpg 1.4) to parse v3 packets. */ if (opt.verbose > 1 && !glo_ctrl.silence_parse_warnings) log_info ("packet(%d) with obsolete version %d\n", pkttype, version); if (list_mode) es_fprintf (listfp, ":key packet: [obsolete version %d]\n", version); pk->version = version; err = gpg_error (GPG_ERR_LEGACY_KEY); goto leave; } else { log_error ("packet(%d) with unknown version %d\n", pkttype, version); if (list_mode) es_fputs (":key packet: [unknown version]\n", listfp); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if (pktlen < (is_v5? 15:11)) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":key packet: [too short]\n", listfp); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } else if (pktlen > MAX_KEY_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fputs (":key packet: [too large]\n", listfp); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } timestamp = read_32 (inp); pktlen -= 4; expiredate = 0; /* have to get it from the selfsignature */ max_expiredate = 0; algorithm = iobuf_get_noeof (inp); pktlen--; if (is_v5) { pkbytes = read_32 (inp); pktlen -= 4; } else pkbytes = 0; if (list_mode) { es_fprintf (listfp, ":%s key packet:\n" "\tversion %d, algo %d, created %lu, expires %lu", pkttype == PKT_PUBLIC_KEY ? "public" : pkttype == PKT_SECRET_KEY ? "secret" : pkttype == PKT_PUBLIC_SUBKEY ? "public sub" : pkttype == PKT_SECRET_SUBKEY ? "secret sub" : "??", version, algorithm, timestamp, expiredate); if (is_v5) es_fprintf (listfp, ", pkbytes %u\n", pkbytes); else es_fprintf (listfp, "\n"); } pk->timestamp = timestamp; pk->expiredate = expiredate; pk->max_expiredate = max_expiredate; pk->hdrbytes = hdrlen; pk->version = version; pk->flags.primary = (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY); pk->pubkey_algo = algorithm; nskey = pubkey_get_nskey (algorithm); npkey = pubkey_get_npkey (algorithm); if (!npkey) { if (list_mode) es_fprintf (listfp, "\tunknown algorithm %d\n", algorithm); unknown_pubkey_warning (algorithm); } if (!npkey) { /* Unknown algorithm - put data into an opaque MPI. */ void *tmpp = read_rest (inp, pktlen); /* Current gcry_mpi_cmp does not handle a (NULL,n>0) nicely and * thus we avoid to create such an MPI. */ pk->pkey[0] = gcry_mpi_set_opaque (NULL, tmpp, tmpp? pktlen * 8 : 0); pktlen = 0; goto leave; } else { for (i = 0; i < npkey; i++) { if ( (algorithm == PUBKEY_ALGO_ECDSA && (i == 0)) || (algorithm == PUBKEY_ALGO_EDDSA && (i == 0)) || (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || i == 2)) || (algorithm == PUBKEY_ALGO_KYBER && (i == 0))) { /* Read the OID (i==0) or the KDF params (i==2). */ err = read_sized_octet_string (inp, &pktlen, pk->pkey+i); } else if (algorithm == PUBKEY_ALGO_KYBER && i == 2) { /* Read the four-octet count prefixed Kyber public key. */ err = read_octet_string (inp, &pktlen, 4, 0, 0, pk->pkey+i); } else { /* Read MPI or SOS. */ unsigned int n = pktlen; if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_EDDSA || algorithm == PUBKEY_ALGO_ECDH || algorithm == PUBKEY_ALGO_KYBER) pk->pkey[i] = sos_read (inp, &n, 0); else pk->pkey[i] = mpi_read (inp, &n, 0); pktlen -= n; if (!pk->pkey[i]) err = gpg_error (GPG_ERR_INV_PACKET); } if (err) goto leave; if (list_mode) { es_fprintf (listfp, "\tpkey[%d]: ", i); mpi_print (listfp, pk->pkey[i], mpi_print_mode); if ((algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_EDDSA || algorithm == PUBKEY_ALGO_ECDH || algorithm == PUBKEY_ALGO_KYBER) && i==0) { char *curve = openpgp_oid_to_str (pk->pkey[0]); const char *name = openpgp_oid_to_curve (curve, 0); es_fprintf (listfp, " %s (%s)", name?name:"", curve); xfree (curve); } es_putc ('\n', listfp); } } } if (list_mode) keyid_from_pk (pk, keyid); if (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY) { struct seckey_info *ski; byte temp[16]; size_t snlen = 0; unsigned int skbytes; if (pktlen < 1) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!pk->seckey_info) { err = gpg_error_from_syserror (); goto leave; } ski->algo = iobuf_get_noeof (inp); pktlen--; if (is_v5) { unsigned int protcount = 0; /* Read the one octet count of the following key-protection * material. Only required in case of unknown values. */ if (!pktlen) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } protcount = iobuf_get_noeof (inp); pktlen--; if (list_mode) es_fprintf (listfp, "\tprotbytes: %u\n", protcount); } if (ski->algo) { ski->is_protected = 1; ski->s2k.count = 0; if (ski->algo == 253) { if (list_mode) es_fprintf (listfp, "\tS2K pseudo algo %d is not yet supported\n", ski->algo); err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; } else if (ski->algo == 254 || ski->algo == 255) { if (pktlen < 3) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ski->sha1chk = (ski->algo == 254); ski->algo = iobuf_get_noeof (inp); pktlen--; /* Note that a ski->algo > 110 is illegal, but I'm not * erroring out here as otherwise there would be no way * to delete such a key. */ ski->s2k.mode = iobuf_get_noeof (inp); pktlen--; ski->s2k.hash_algo = iobuf_get_noeof (inp); pktlen--; /* Check for the special GNU extension. */ if (ski->s2k.mode == 101) { for (i = 0; i < 4 && pktlen; i++, pktlen--) temp[i] = iobuf_get_noeof (inp); if (i < 4 || memcmp (temp, "GNU", 3)) { if (list_mode) es_fprintf (listfp, "\tunknown S2K %d\n", ski->s2k.mode); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } /* Here we know that it is a GNU extension. What * follows is the GNU protection mode: All values * have special meanings and they are mapped to MODE * with a base of 1000. */ ski->s2k.mode = 1000 + temp[3]; } /* Read the salt. */ if (ski->s2k.mode == 3 || ski->s2k.mode == 1) { for (i = 0; i < 8 && pktlen; i++, pktlen--) temp[i] = iobuf_get_noeof (inp); if (i < 8) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } memcpy (ski->s2k.salt, temp, 8); } /* Check the mode. */ switch (ski->s2k.mode) { case 0: if (list_mode) es_fprintf (listfp, "\tsimple S2K"); break; case 1: if (list_mode) es_fprintf (listfp, "\tsalted S2K"); break; case 3: if (list_mode) es_fprintf (listfp, "\titer+salt S2K"); break; case 1001: if (list_mode) es_fprintf (listfp, "\tgnu-dummy"); break; case 1002: if (list_mode) es_fprintf (listfp, "\tgnu-divert-to-card"); break; case 1003: if (list_mode) es_fprintf (listfp, "\tgnu-mode1003"); break; default: if (list_mode) es_fprintf (listfp, "\tunknown %sS2K %d\n", ski->s2k.mode < 1000 ? "" : "GNU ", ski->s2k.mode); err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } /* Print some info. */ if (list_mode && ski->s2k.mode != 1003) { es_fprintf (listfp, ", algo: %d,%s hash: %d", ski->algo, ski->sha1chk ? " SHA1 protection," : " simple checksum,", ski->s2k.hash_algo); if (ski->s2k.mode == 1 || ski->s2k.mode == 3) { es_fprintf (listfp, ", salt: "); es_write_hexstring (listfp, ski->s2k.salt, 8, 0, NULL); } } if (list_mode) es_putc ('\n', listfp); /* Read remaining protection parameters. */ if (ski->s2k.mode == 3) { if (pktlen < 1) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ski->s2k.count = iobuf_get_noeof (inp); pktlen--; if (list_mode) es_fprintf (listfp, "\tprotect count: %lu (%lu)\n", (ulong)S2K_DECODE_COUNT ((ulong)ski->s2k.count), (ulong) ski->s2k.count); } else if (ski->s2k.mode == 1002) { /* Read the serial number. */ if (pktlen < 1) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } snlen = iobuf_get (inp); pktlen--; if (pktlen < snlen || snlen == (size_t)(-1)) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } } } else /* Old version; no S2K, so we set mode to 0, hash MD5. */ { /* Note that a ski->algo > 110 is illegal, but I'm not erroring on it here as otherwise there would be no way to delete such a key. */ ski->s2k.mode = 0; ski->s2k.hash_algo = DIGEST_ALGO_MD5; if (list_mode) es_fprintf (listfp, "\tprotect algo: %d (hash algo: %d)\n", ski->algo, ski->s2k.hash_algo); } /* It is really ugly that we don't know the size * of the IV here in cases we are not aware of the algorithm. * so a * ski->ivlen = cipher_get_blocksize (ski->algo); * won't work. The only solution I see is to hardwire it. * NOTE: if you change the ivlen above 16, don't forget to * enlarge temp. * FIXME: For v5 keys we can deduce this info! */ ski->ivlen = openpgp_cipher_blocklen (ski->algo); log_assert (ski->ivlen <= sizeof (temp)); if (ski->s2k.mode == 1001 || ski->s2k.mode == 1003) ski->ivlen = 0; else if (ski->s2k.mode == 1002) ski->ivlen = snlen < 16 ? snlen : 16; if (pktlen < ski->ivlen) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } for (i = 0; i < ski->ivlen; i++, pktlen--) temp[i] = iobuf_get_noeof (inp); if (list_mode && ski->s2k.mode != 1003) { es_fprintf (listfp, ski->s2k.mode == 1002 ? "\tserial-number: " : "\tprotect IV: "); for (i = 0; i < ski->ivlen; i++) es_fprintf (listfp, " %02x", temp[i]); es_putc ('\n', listfp); } memcpy (ski->iv, temp, ski->ivlen); } /* Skip count of secret key material. */ if (is_v5) { if (pktlen < 4) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } skbytes = read_32 (inp); pktlen -= 4; if (list_mode) es_fprintf (listfp, "\tskbytes: %u\n", skbytes); } /* It does not make sense to read it into secure memory. * If the user is so careless, not to protect his secret key, * we can assume, that he operates an open system :=(. * So we put the key into secure memory when we unprotect it. */ if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002) { /* Better set some dummy stuff here. */ pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, xstrdup ("dummydata"), 10 * 8); pktlen = 0; } else if (ski->s2k.mode == 1003) { void *tmpp; if (pktlen < 2) /* At least two bytes for parenthesis. */ { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } tmpp = read_rest (inp, pktlen); if (list_mode) { if (mpi_print_mode) { char *tmpsxp = canon_sexp_to_string (tmpp, pktlen); es_fprintf (listfp, "\tskey[%d]: %s\n", npkey, tmpsxp? trim_trailing_spaces (tmpsxp) /* */: "[invalid S-expression]"); xfree (tmpsxp); } else es_fprintf (listfp, "\tskey[%d]: [s-expression %lu octets]\n", npkey, pktlen); } pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, tmpp, tmpp? pktlen * 8 : 0); pktlen = 0; } else if (ski->is_protected) { void *tmpp; if (pktlen < 2) /* At least two bytes for the length. */ { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } /* Ugly: The length is encrypted too, so we read all stuff * up to the end of the packet into the first SKEY * element. * FIXME: We can do better for v5 keys. */ tmpp = read_rest (inp, pktlen); pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, tmpp, tmpp? pktlen * 8 : 0); /* Mark that MPI as protected - we need this information for * importing a key. The OPAQUE flag can't be used because * we also store public EdDSA values in opaque MPIs. */ if (pk->pkey[npkey]) gcry_mpi_set_flag (pk->pkey[npkey], GCRYMPI_FLAG_USER1); pktlen = 0; if (list_mode) es_fprintf (listfp, "\tskey[%d]: [v4 protected]\n", npkey); } else { /* Not encrypted. */ for (i = npkey; i < nskey; i++) { if (pktlen < 2) /* At least two bytes for the length. */ { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } if (algorithm == PUBKEY_ALGO_KYBER && i == npkey+1) { err = read_octet_string (inp, &pktlen, 4, 0, 1, pk->pkey+i); if (err) goto leave; } else { unsigned int n = pktlen; if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_EDDSA || algorithm == PUBKEY_ALGO_ECDH || algorithm == PUBKEY_ALGO_KYBER) pk->pkey[i] = sos_read (inp, &n, 0); else pk->pkey[i] = mpi_read (inp, &n, 0); pktlen -= n; } if (list_mode) { es_fprintf (listfp, "\tskey[%d]: ", i); mpi_print (listfp, pk->pkey[i], mpi_print_mode); es_putc ('\n', listfp); } if (!pk->pkey[i]) err = gpg_error (GPG_ERR_INV_PACKET); } if (err) goto leave; if (pktlen < 2) { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ski->csum = read_16 (inp); pktlen -= 2; if (list_mode) es_fprintf (listfp, "\tchecksum: %04hx\n", ski->csum); } } /* Note that KEYID below has been initialized above in list_mode. */ if (list_mode) es_fprintf (listfp, "\tkeyid: %08lX%08lX\n", (ulong) keyid[0], (ulong) keyid[1]); leave: iobuf_skip_rest (inp, pktlen, 0); return err; } /* Attribute subpackets have the same format as v4 signature subpackets. This is not part of OpenPGP, but is done in several versions of PGP nevertheless. */ int parse_attribute_subpkts (PKT_user_id * uid) { size_t n; int count = 0; struct user_attribute *attribs = NULL; const byte *buffer = uid->attrib_data; int buflen = uid->attrib_len; byte type; xfree (uid->attribs); while (buflen) { n = *buffer++; buflen--; if (n == 255) /* 4 byte length header. */ { if (buflen < 4) goto too_short; n = buf32_to_size_t (buffer); buffer += 4; buflen -= 4; } else if (n >= 192) /* 2 byte special encoded length header. */ { if (buflen < 2) goto too_short; n = ((n - 192) << 8) + *buffer + 192; buffer++; buflen--; } if (buflen < n) goto too_short; if (!n) { /* Too short to encode the subpacket type. */ if (opt.verbose) log_info ("attribute subpacket too short\n"); break; } attribs = xrealloc (attribs, (count + 1) * sizeof (struct user_attribute)); memset (&attribs[count], 0, sizeof (struct user_attribute)); type = *buffer; buffer++; buflen--; n--; attribs[count].type = type; attribs[count].data = buffer; attribs[count].len = n; buffer += n; buflen -= n; count++; } uid->attribs = attribs; uid->numattribs = count; return count; too_short: if (opt.verbose && !glo_ctrl.silence_parse_warnings) log_info ("buffer shorter than attribute subpacket\n"); uid->attribs = attribs; uid->numattribs = count; return count; } static int parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { byte *p; /* Cap the size of a user ID at 2k: a value absurdly large enough that there is no sane user ID string (which is printable text as of RFC2440bis) that won't fit in it, but yet small enough to avoid allocation problems. A large pktlen may not be allocatable, and a very large pktlen could actually cause our allocation to wrap around in xmalloc to a small number. */ if (pktlen > MAX_UID_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fprintf (listfp, ":user ID packet: [too large]\n"); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id + pktlen); packet->pkt.user_id->len = pktlen; packet->pkt.user_id->ref = 1; p = packet->pkt.user_id->name; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); *p = 0; if (list_mode) { int n = packet->pkt.user_id->len; es_fprintf (listfp, ":user ID packet: \""); /* fixme: Hey why don't we replace this with es_write_sanitized?? */ for (p = packet->pkt.user_id->name; n; p++, n--) { if (*p >= ' ' && *p <= 'z') es_putc (*p, listfp); else es_fprintf (listfp, "\\x%02x", *p); } es_fprintf (listfp, "\"\n"); } return 0; } void make_attribute_uidname (PKT_user_id * uid, size_t max_namelen) { log_assert (max_namelen > 70); if (uid->numattribs <= 0) sprintf (uid->name, "[bad attribute packet of size %lu]", uid->attrib_len); else if (uid->numattribs > 1) sprintf (uid->name, "[%d attributes of size %lu]", uid->numattribs, uid->attrib_len); else { /* Only one attribute, so list it as the "user id" */ if (uid->attribs->type == ATTRIB_IMAGE) { u32 len; byte type; if (parse_image_header (uid->attribs, &type, &len)) sprintf (uid->name, "[%.20s image of size %lu]", image_type_to_string (type, 1), (ulong) len); else sprintf (uid->name, "[invalid image]"); } else sprintf (uid->name, "[unknown attribute of size %lu]", (ulong) uid->attribs->len); } uid->len = strlen (uid->name); } static int parse_attribute (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { byte *p; (void) pkttype; /* We better cap the size of an attribute packet to make DoS not too easy. 16MB should be more then enough for one attribute packet (ie. a photo). */ if (pktlen > MAX_ATTR_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fprintf (listfp, ":attribute packet: [too large]\n"); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } #define EXTRA_UID_NAME_SPACE 71 packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id + EXTRA_UID_NAME_SPACE); packet->pkt.user_id->ref = 1; packet->pkt.user_id->attrib_data = xmalloc (pktlen? pktlen:1); packet->pkt.user_id->attrib_len = pktlen; p = packet->pkt.user_id->attrib_data; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); /* Now parse out the individual attribute subpackets. This is somewhat pointless since there is only one currently defined attribute type (jpeg), but it is correct by the spec. */ parse_attribute_subpkts (packet->pkt.user_id); make_attribute_uidname (packet->pkt.user_id, EXTRA_UID_NAME_SPACE); if (list_mode) { es_fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name); } return 0; } static int parse_comment (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { byte *p; /* Cap comment packet at a reasonable value to avoid an integer overflow in the malloc below. Comment packets are actually not anymore define my OpenPGP and we even stopped to use our private comment packet. */ if (pktlen > MAX_COMMENT_PACKET_LENGTH) { log_error ("packet(%d) too large\n", pkttype); if (list_mode) es_fprintf (listfp, ":%scomment packet: [too large]\n", pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : ""); iobuf_skip_rest (inp, pktlen, 0); return GPG_ERR_INV_PACKET; } packet->pkt.comment = xmalloc (sizeof *packet->pkt.comment + pktlen - 1); packet->pkt.comment->len = pktlen; p = packet->pkt.comment->data; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); if (list_mode) { int n = packet->pkt.comment->len; es_fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : ""); for (p = packet->pkt.comment->data; n; p++, n--) { if (*p >= ' ' && *p <= 'z') es_putc (*p, listfp); else es_fprintf (listfp, "\\x%02x", *p); } es_fprintf (listfp, "\"\n"); } return 0; } /* Parse a ring trust packet RFC4880 (5.10). * * This parser is special in that the packet is not stored as a packet * but its content is merged into the previous packet. */ static gpg_error_t parse_ring_trust (parse_packet_ctx_t ctx, unsigned long pktlen) { gpg_error_t err; iobuf_t inp = ctx->inp; PKT_ring_trust rt = {0}; int c; int not_gpg = 0; if (!pktlen) { if (list_mode) es_fprintf (listfp, ":trust packet: empty\n"); err = 0; goto leave; } c = iobuf_get_noeof (inp); pktlen--; rt.trustval = c; if (pktlen) { if (!c) { c = iobuf_get_noeof (inp); /* We require that bit 7 of the sigcache is 0 (easier * eof handling). */ if (!(c & 0x80)) rt.sigcache = c; } else iobuf_get_noeof (inp); /* Dummy read. */ pktlen--; } /* Next is the optional subtype. */ if (pktlen > 3) { char tmp[4]; tmp[0] = iobuf_get_noeof (inp); tmp[1] = iobuf_get_noeof (inp); tmp[2] = iobuf_get_noeof (inp); tmp[3] = iobuf_get_noeof (inp); pktlen -= 4; if (!memcmp (tmp, "gpg", 3)) rt.subtype = tmp[3]; else not_gpg = 1; } /* If it is a key or uid subtype read the remaining data. */ if ((rt.subtype == RING_TRUST_KEY || rt.subtype == RING_TRUST_UID) && pktlen >= 6 ) { int i; unsigned int namelen; rt.keyorg = iobuf_get_noeof (inp); pktlen--; rt.keyupdate = read_32 (inp); pktlen -= 4; namelen = iobuf_get_noeof (inp); pktlen--; if (namelen && pktlen) { rt.url = xtrymalloc (namelen + 1); if (!rt.url) { err = gpg_error_from_syserror (); goto leave; } for (i = 0; pktlen && i < namelen; pktlen--, i++) rt.url[i] = iobuf_get_noeof (inp); rt.url[i] = 0; } } if (list_mode) { if (rt.subtype == RING_TRUST_SIG) es_fprintf (listfp, ":trust packet: sig flag=%02x sigcache=%02x\n", rt.trustval, rt.sigcache); else if (rt.subtype == RING_TRUST_UID || rt.subtype == RING_TRUST_KEY) { unsigned char *p; es_fprintf (listfp, ":trust packet: %s upd=%lu src=%d%s", (rt.subtype == RING_TRUST_UID? "uid" : "key"), (unsigned long)rt.keyupdate, rt.keyorg, (rt.url? " url=":"")); if (rt.url) { for (p = rt.url; *p; p++) { if (*p >= ' ' && *p <= 'z') es_putc (*p, listfp); else es_fprintf (listfp, "\\x%02x", *p); } } es_putc ('\n', listfp); } else if (not_gpg) es_fprintf (listfp, ":trust packet: not created by gpg\n"); else es_fprintf (listfp, ":trust packet: subtype=%02x\n", rt.subtype); } /* Now transfer the data to the respective packet. Do not do this * if SKIP_META is set. */ if (!ctx->last_pkt.pkt.generic || ctx->skip_meta) ; else if (rt.subtype == RING_TRUST_SIG && ctx->last_pkt.pkttype == PKT_SIGNATURE) { PKT_signature *sig = ctx->last_pkt.pkt.signature; if ((rt.sigcache & 1)) { sig->flags.checked = 1; sig->flags.valid = !!(rt.sigcache & 2); } } else if (rt.subtype == RING_TRUST_UID && (ctx->last_pkt.pkttype == PKT_USER_ID || ctx->last_pkt.pkttype == PKT_ATTRIBUTE)) { PKT_user_id *uid = ctx->last_pkt.pkt.user_id; uid->keyorg = rt.keyorg; uid->keyupdate = rt.keyupdate; uid->updateurl = rt.url; rt.url = NULL; } else if (rt.subtype == RING_TRUST_KEY && (ctx->last_pkt.pkttype == PKT_PUBLIC_KEY || ctx->last_pkt.pkttype == PKT_SECRET_KEY)) { PKT_public_key *pk = ctx->last_pkt.pkt.public_key; pk->keyorg = rt.keyorg; pk->keyupdate = rt.keyupdate; pk->updateurl = rt.url; rt.url = NULL; } err = 0; leave: xfree (rt.url); free_packet (NULL, ctx); /* This sets ctx->last_pkt to NULL. */ iobuf_skip_rest (inp, pktlen, 0); return err; } static int parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt, int new_ctb, int partial) { int rc = 0; int mode, namelen; PKT_plaintext *pt; byte *p; int c, i; if (!partial && pktlen < 6) { log_error ("packet(%d) too short (%lu)\n", pkttype, (ulong) pktlen); if (list_mode) es_fputs (":literal data packet: [too short]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } mode = iobuf_get_noeof (inp); if (pktlen) pktlen--; namelen = iobuf_get_noeof (inp); if (pktlen) pktlen--; /* Note that namelen will never exceed 255 bytes. */ pt = pkt->pkt.plaintext = xmalloc (sizeof *pkt->pkt.plaintext + namelen - 1); pt->new_ctb = new_ctb; pt->mode = mode; pt->namelen = namelen; pt->is_partial = partial; if (pktlen) { for (i = 0; pktlen > 4 && i < namelen; pktlen--, i++) pt->name[i] = iobuf_get_noeof (inp); } else { for (i = 0; i < namelen; i++) if ((c = iobuf_get (inp)) == -1) break; else pt->name[i] = c; } /* Fill up NAME so that a check with valgrind won't complain about * reading from uninitialized memory. This case may be triggred by * corrupted packets. */ for (; i < namelen; i++) pt->name[i] = 0; pt->timestamp = read_32 (inp); if (pktlen) pktlen -= 4; pt->len = pktlen; pt->buf = inp; if (list_mode) { es_fprintf (listfp, ":literal data packet:\n" "\tmode %c (%X), created %lu, name=\"", mode >= ' ' && mode < 'z' ? mode : '?', mode, (ulong) pt->timestamp); for (p = pt->name, i = 0; i < namelen; p++, i++) { if (*p >= ' ' && *p <= 'z') es_putc (*p, listfp); else es_fprintf (listfp, "\\x%02x", *p); } es_fprintf (listfp, "\",\n\traw data: "); if (partial) es_fprintf (listfp, "unknown length\n"); else es_fprintf (listfp, "%lu bytes\n", (ulong) pt->len); } leave: return rc; } static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt, int new_ctb) { PKT_compressed *zd; /* PKTLEN is here 0, but data follows (this should be the last object in a file or the compress algorithm should know the length). */ (void) pkttype; (void) pktlen; zd = pkt->pkt.compressed = xmalloc (sizeof *pkt->pkt.compressed); zd->algorithm = iobuf_get_noeof (inp); zd->len = 0; /* not used */ zd->new_ctb = new_ctb; zd->buf = inp; if (list_mode) es_fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm); return 0; } static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt, int new_ctb, int partial) { int rc = 0; PKT_encrypted *ed; unsigned long orig_pktlen = pktlen; ed = pkt->pkt.encrypted = xmalloc (sizeof *pkt->pkt.encrypted); /* ed->len is set below. */ ed->extralen = 0; /* Unknown here; only used in build_packet. */ ed->buf = NULL; ed->new_ctb = new_ctb; ed->is_partial = partial; ed->aead_algo = 0; ed->cipher_algo = 0; /* Only used with AEAD. */ ed->chunkbyte = 0; /* Only used with AEAD. */ if (pkttype == PKT_ENCRYPTED_MDC) { /* Fixme: add some pktlen sanity checks. */ int version; version = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; if (version != 1) { log_error ("encrypted_mdc packet with unknown version %d\n", version); if (list_mode) es_fputs (":encrypted data packet: [unknown version]\n", listfp); /*skip_rest(inp, pktlen); should we really do this? */ rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ed->mdc_method = DIGEST_ALGO_SHA1; } else ed->mdc_method = 0; /* A basic sanity check. We need at least an 8 byte IV plus the 2 detection bytes. Note that we don't known the algorithm and thus we may only check against the minimum blocksize. */ if (orig_pktlen && pktlen < 10) { /* Actually this is blocksize+2. */ log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":encrypted data packet: [too short]\n", listfp); rc = GPG_ERR_INV_PACKET; iobuf_skip_rest (inp, pktlen, partial); goto leave; } /* Store the remaining length of the encrypted data (i.e. without the MDC version number but with the IV etc.). This value is required during decryption. */ ed->len = pktlen; if (list_mode) { if (orig_pktlen) es_fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n", orig_pktlen); else es_fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n"); if (ed->mdc_method) es_fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method); } ed->buf = inp; leave: return rc; } /* Note, that this code is not anymore used in real life because the MDC checking is now done right after the decryption in decrypt_data. */ static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt, int new_ctb) { int rc = 0; PKT_mdc *mdc; byte *p; (void) pkttype; mdc = pkt->pkt.mdc = xmalloc (sizeof *pkt->pkt.mdc); if (list_mode) es_fprintf (listfp, ":mdc packet: length=%lu\n", pktlen); if (!new_ctb || pktlen != 20) { log_error ("mdc_packet with invalid encoding\n"); rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } p = mdc->hash; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); leave: return rc; } static gpg_error_t parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen, PACKET *pkt, int partial) { int rc = 0; PKT_encrypted *ed; unsigned long orig_pktlen = pktlen; int version; ed = pkt->pkt.encrypted = xtrymalloc (sizeof *pkt->pkt.encrypted); if (!ed) return gpg_error_from_syserror (); ed->len = 0; ed->extralen = 0; /* (only used in build_packet.) */ ed->buf = NULL; ed->new_ctb = 1; /* (packet number requires a new CTB anyway.) */ ed->is_partial = partial; ed->mdc_method = 0; /* A basic sanity check. We need one version byte, one algo byte, * one aead algo byte, one chunkbyte, at least 15 byte IV. */ if (orig_pktlen && pktlen < 19) { log_error ("packet(%d) too short\n", pkttype); if (list_mode) es_fputs (":aead encrypted packet: [too short]\n", listfp); rc = gpg_error (GPG_ERR_INV_PACKET); iobuf_skip_rest (inp, pktlen, partial); goto leave; } version = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; if (version != 1) { log_error ("aead encrypted packet with unknown version %d\n", version); if (list_mode) es_fputs (":aead encrypted packet: [unknown version]\n", listfp); /*skip_rest(inp, pktlen); should we really do this? */ rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } ed->cipher_algo = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; ed->aead_algo = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; ed->chunkbyte = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; /* Store the remaining length of the encrypted data. We read the * rest during decryption. */ ed->len = pktlen; if (list_mode) { es_fprintf (listfp, ":aead encrypted packet: cipher=%u aead=%u cb=%u\n", ed->cipher_algo, ed->aead_algo, ed->chunkbyte); if (orig_pktlen) es_fprintf (listfp, "\tlength: %lu\n", orig_pktlen); else es_fprintf (listfp, "\tlength: unknown\n"); } ed->buf = inp; leave: return rc; } /* * This packet is internally generated by us (in armor.c) to transfer * some information to the lower layer. To make sure that this packet * is really a GPG faked one and not one coming from outside, we * first check that there is a unique tag in it. * * The format of such a control packet is: * n byte session marker * 1 byte control type CTRLPKT_xxxxx * m byte control data */ static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int partial) { byte *p; const byte *sesmark; size_t sesmarklen; int i; (void) pkttype; if (list_mode) es_fprintf (listfp, ":packet 63: length %lu ", pktlen); sesmark = get_session_marker (&sesmarklen); if (pktlen < sesmarklen + 1) /* 1 is for the control bytes */ goto skipit; for (i = 0; i < sesmarklen; i++, pktlen--) { if (sesmark[i] != iobuf_get_noeof (inp)) goto skipit; } if (pktlen > 4096) goto skipit; /* Definitely too large. We skip it to avoid an overflow in the malloc. */ if (list_mode) es_fputs ("- gpg control packet", listfp); packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control + pktlen - 1); packet->pkt.gpg_control->control = iobuf_get_noeof (inp); pktlen--; packet->pkt.gpg_control->datalen = pktlen; p = packet->pkt.gpg_control->data; for (; pktlen; pktlen--, p++) *p = iobuf_get_noeof (inp); return 0; skipit: if (list_mode) { int c; i = 0; es_fprintf (listfp, "- private (rest length %lu)\n", pktlen); if (partial) { while ((c = iobuf_get (inp)) != -1) dump_hex_line (c, &i); } else { for (; pktlen; pktlen--) { dump_hex_line ((c = iobuf_get (inp)), &i); if (c == -1) break; } } es_putc ('\n', listfp); } iobuf_skip_rest (inp, pktlen, 0); return gpg_error (GPG_ERR_INV_PACKET); } /* Create a GPG control packet to be used internally as a placeholder. */ PACKET * create_gpg_control (ctrlpkttype_t type, const byte * data, size_t datalen) { PACKET *packet; byte *p; if (!data) datalen = 0; packet = xmalloc (sizeof *packet); init_packet (packet); packet->pkttype = PKT_GPG_CONTROL; packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control + datalen); packet->pkt.gpg_control->control = type; packet->pkt.gpg_control->datalen = datalen; p = packet->pkt.gpg_control->data; for (; datalen; datalen--, p++) *p = *data++; return packet; } diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 3e9daa963..873b864b5 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -1,505 +1,515 @@ /* pubkey-enc.c - Process a public key encoded packet. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "packet.h" #include "keydb.h" #include "trustdb.h" #include "../common/status.h" #include "options.h" #include "main.h" #include "../common/i18n.h" #include "pkglue.h" #include "call-agent.h" #include "../common/host2net.h" #include "../common/compliance.h" static gpg_error_t get_it (ctrl_t ctrl, struct pubkey_enc_list *k, DEK *dek, PKT_public_key *sk, u32 *keyid); /* Check that the given algo is mentioned in one of the valid user-ids. */ static int is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo) { kbnode_t k; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = k->pkt->pkt.user_id; prefitem_t *prefs = uid->prefs; if (uid->created && prefs && !uid->flags.revoked && !uid->flags.expired) { for (; prefs->type; prefs++) if (prefs->type == type && prefs->value == algo) return 1; } } } return 0; } /* * Get the session key from a pubkey enc packet and return it in DEK, * which should have been allocated in secure memory by the caller. */ gpg_error_t get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek) { PKT_public_key *sk = NULL; gpg_error_t err; void *enum_context = NULL; u32 keyid[2]; int search_for_secret_keys = 1; struct pubkey_enc_list *k; if (DBG_CLOCK) log_clock ("get_session_key enter"); while (search_for_secret_keys) { sk = xmalloc_clear (sizeof *sk); err = enum_secret_keys (ctrl, &enum_context, sk); if (err) break; /* Check compliance. */ if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, sk->pubkey_algo, 0, sk->pkey, nbits_from_pk (sk), NULL)) { log_info (_("key %s is not suitable for decryption" " in %s mode\n"), keystr_from_pk (sk), gnupg_compliance_option_string (opt.compliance)); continue; } /* FIXME: The list needs to be sorted so that we try the keys in * an appropriate order. For example: * - On-disk keys w/o protection * - On-disk keys with a cached passphrase * - On-card keys of an active card * - On-disk keys with protection * - On-card keys from cards which are not plugged it. Here a * cancel-all button should stop asking for other cards. * Without any anonymous keys the sorting can be skipped. */ for (k = list; k; k = k->next) { if (!(k->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E || k->pubkey_algo == PUBKEY_ALGO_ECDH + || k->pubkey_algo == PUBKEY_ALGO_KYBER || k->pubkey_algo == PUBKEY_ALGO_RSA || k->pubkey_algo == PUBKEY_ALGO_RSA_E || k->pubkey_algo == PUBKEY_ALGO_ELGAMAL)) continue; if (openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC)) continue; if (sk->pubkey_algo != k->pubkey_algo) continue; keyid_from_pk (sk, keyid); if (!k->keyid[0] && !k->keyid[1]) { if (opt.skip_hidden_recipients) continue; if (!opt.quiet) log_info (_("anonymous recipient; trying secret key %s ...\n"), keystr (keyid)); } else if (opt.try_all_secrets || (k->keyid[0] == keyid[0] && k->keyid[1] == keyid[1])) { if (!opt.quiet && !(sk->pubkey_usage & PUBKEY_USAGE_ENC)) log_info (_("used key is not marked for encryption use.\n")); } else continue; err = get_it (ctrl, k, dek, sk, keyid); k->result = err; if (!err) { if (!opt.quiet && !k->keyid[0] && !k->keyid[1]) { log_info (_("okay, we are the anonymous recipient.\n")); if (!(sk->pubkey_usage & PUBKEY_USAGE_ENC)) log_info (_("used key is not marked for encryption use.\n") ); } search_for_secret_keys = 0; break; } else if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) { search_for_secret_keys = 0; break; /* Don't try any more secret keys. */ } } } enum_secret_keys (ctrl, &enum_context, NULL); /* free context */ if (gpg_err_code (err) == GPG_ERR_EOF) { err = gpg_error (GPG_ERR_NO_SECKEY); /* Return the last specific error, if any. */ for (k = list; k; k = k->next) if (k->result != -1) err = k->result; } if (DBG_CLOCK) log_clock ("get_session_key leave"); return err; } static gpg_error_t get_it (ctrl_t ctrl, struct pubkey_enc_list *enc, DEK *dek, PKT_public_key *sk, u32 *keyid) { gpg_error_t err; byte *frame = NULL; unsigned int n; size_t nframe; u16 csum, csum2; int padding; gcry_sexp_t s_data; char *desc; char *keygrip; byte fp[MAX_FINGERPRINT_LEN]; if (DBG_CLOCK) log_clock ("decryption start"); /* Get the keygrip. */ err = hexkeygrip_from_pk (sk, &keygrip); if (err) goto leave; /* Convert the data to an S-expression. */ if (sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL || sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E) { if (!enc->data[0] || !enc->data[1]) err = gpg_error (GPG_ERR_BAD_MPI); else err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))", enc->data[0], enc->data[1]); } else if (sk->pubkey_algo == PUBKEY_ALGO_RSA || sk->pubkey_algo == PUBKEY_ALGO_RSA_E) { if (!enc->data[0]) err = gpg_error (GPG_ERR_BAD_MPI); else err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))", enc->data[0]); } else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) { if (!enc->data[0] || !enc->data[1]) err = gpg_error (GPG_ERR_BAD_MPI); else err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))", enc->data[1], enc->data[0]); } + else if (sk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + if (!enc->data[0] || !enc->data[1] || !enc->data[2] || !enc->data[3]) + err = gpg_error (GPG_ERR_BAD_MPI); + else + err = gcry_sexp_build (&s_data, NULL, + "(enc-val(pqc(e%m)(k%m)(s%m)(fixed-info%s)))", + enc->data[0], enc->data[1], enc->data[3], + "\x1d" /*PUBKEY_ALGO_KYBER*/); + } else err = gpg_error (GPG_ERR_BUG); if (err) goto leave; if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) fingerprint_from_pk (sk, fp, NULL); /* Decrypt. */ desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1); - /*FIXME: Support dual keys. */ err = agent_pkdecrypt (NULL, keygrip, desc, sk->keyid, sk->main_keyid, sk->pubkey_algo, s_data, &frame, &nframe, &padding); xfree (desc); gcry_sexp_release (s_data); if (err) goto leave; /* Now get the DEK (data encryption key) from the frame * * Old versions encode the DEK in this format (msb is left): * * 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2 * * Later versions encode the DEK like this: * * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) * * (mpi_get_buffer already removed the leading zero). * * RND are non-zero randow bytes. * A is the cipher algorithm * DEK is the encryption key (session key) with length k * CSUM */ if (DBG_CRYPTO) log_printhex (frame, nframe, "DEK frame:"); n = 0; if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) { gcry_mpi_t decoded; /* At the beginning the frame are the bytes of shared point MPI. */ err = pk_ecdh_decrypt (&decoded, fp, enc->data[1]/*encr data as an MPI*/, frame, nframe, sk->pkey); if(err) goto leave; xfree (frame); err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, decoded); mpi_release (decoded); if (err) goto leave; /* Now the frame are the bytes decrypted but padded session key. */ if (!nframe || nframe <= 8 || frame[nframe-1] > nframe) { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } nframe -= frame[nframe-1]; /* Remove padding. */ log_assert (!n); /* (used just below) */ } else { if (padding) { if (n + 7 > nframe) { err = gpg_error (GPG_ERR_WRONG_SECKEY); 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 (!frame[n]) n++; if (frame[n] == 1 && frame[nframe - 1] == 2) { log_info (_("old encoding of the DEK is not supported\n")); err = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } if (frame[n] != 2) /* Something went wrong. */ { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */ ; n++; /* Skip the zero byte. */ } } if (n + 4 > nframe) { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } dek->keylen = nframe - (n + 1) - 2; dek->algo = frame[n++]; err = openpgp_cipher_test_algo (dek->algo); if (err) { if (!opt.quiet && gpg_err_code (err) == GPG_ERR_CIPHER_ALGO) { log_info (_("cipher algorithm %d%s is unknown or disabled\n"), dek->algo, dek->algo == CIPHER_ALGO_IDEA ? " (IDEA)" : ""); } dek->algo = 0; goto leave; } if (dek->keylen != openpgp_cipher_get_algo_keylen (dek->algo)) { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } /* Copy the key to DEK and compare the checksum. */ csum = buf16_to_u16 (frame+nframe-2); memcpy (dek->key, frame + n, dek->keylen); for (csum2 = 0, n = 0; n < dek->keylen; n++) csum2 += dek->key[n]; if (csum != csum2) { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } if (DBG_CLOCK) log_clock ("decryption ready"); if (DBG_CRYPTO) log_printhex (dek->key, dek->keylen, "DEK is:"); /* Check that the algo is in the preferences and whether it has * expired. Also print a status line with the key's fingerprint. */ { PKT_public_key *pk = NULL; PKT_public_key *mainpk = NULL; KBNODE pkb = get_pubkeyblock (ctrl, keyid); if (!pkb) { err = -1; log_error ("oops: public key not found for preference check\n"); } else if (pkb->pkt->pkt.public_key->selfsigversion > 3 && dek->algo != CIPHER_ALGO_3DES && !opt.quiet && !is_algo_in_prefs (pkb, PREFTYPE_SYM, dek->algo)) log_info (_("WARNING: cipher algorithm %s not found in recipient" " preferences\n"), openpgp_cipher_algo_name (dek->algo)); if (!err) { kbnode_t k; int first = 1; for (k = pkb; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 aki[2]; if (first) { first = 0; mainpk = k->pkt->pkt.public_key; } keyid_from_pk (k->pkt->pkt.public_key, aki); if (aki[0] == keyid[0] && aki[1] == keyid[1]) { pk = k->pkt->pkt.public_key; break; } } } if (!pk) BUG (); if (pk->expiredate && pk->expiredate <= make_timestamp ()) { log_info (_("Note: secret key %s expired at %s\n"), keystr (keyid), asctimestamp (pk->expiredate)); } } if (pk && pk->flags.revoked) { log_info (_("Note: key has been revoked")); log_printf ("\n"); show_revocation_reason (ctrl, pk, 1); } if (is_status_enabled () && pk && mainpk) { char pkhex[MAX_FINGERPRINT_LEN*2+1]; char mainpkhex[MAX_FINGERPRINT_LEN*2+1]; hexfingerprint (pk, pkhex, sizeof pkhex); hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex); /* Note that we do not want to create a trustdb just for * getting the ownertrust: If there is no trustdb there can't * be ulitmately trusted key anyway and thus the ownertrust * value is irrelevant. */ write_status_printf (STATUS_DECRYPTION_KEY, "%s %s %c", pkhex, mainpkhex, get_ownertrust_info (ctrl, mainpk, 1)); } release_kbnode (pkb); err = 0; } leave: xfree (frame); xfree (keygrip); return err; } /* * Get the session key from the given string. * String is supposed to be formatted as this: * : */ gpg_error_t get_override_session_key (DEK *dek, const char *string) { const char *s; int i; if (!string) return GPG_ERR_BAD_KEY; dek->algo = atoi (string); if (dek->algo < 1) return GPG_ERR_BAD_KEY; if (!(s = strchr (string, ':'))) return GPG_ERR_BAD_KEY; s++; for (i = 0; i < DIM (dek->key) && *s; i++, s += 2) { int c = hextobyte (s); if (c == -1) return GPG_ERR_BAD_KEY; dek->key[i] = c; } if (*s) return GPG_ERR_BAD_KEY; dek->keylen = i; return 0; }