diff --git a/common/compliance.c b/common/compliance.c index 0c785799e..48eeb1fe7 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -1,590 +1,638 @@ /* compliance.c - Functions for compliance modi * Copyright (C) 2017 g10 Code GmbH * Copyright (C) 2017 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file 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 "openpgpdefs.h" #include "logging.h" #include "util.h" #include "i18n.h" #include "compliance.h" static int initialized; static int module; /* Initializes the module. Must be called with the current * GNUPG_MODULE_NAME. Checks a few invariants, and tunes the policies * for the given module. */ void gnupg_initialize_compliance (int gnupg_module_name) { log_assert (! initialized); /* We accept both OpenPGP-style and gcrypt-style algorithm ids. * Assert that they are compatible. */ log_assert ((int) GCRY_PK_RSA == (int) PUBKEY_ALGO_RSA); log_assert ((int) GCRY_PK_RSA_E == (int) PUBKEY_ALGO_RSA_E); log_assert ((int) GCRY_PK_RSA_S == (int) PUBKEY_ALGO_RSA_S); log_assert ((int) GCRY_PK_ELG_E == (int) PUBKEY_ALGO_ELGAMAL_E); log_assert ((int) GCRY_PK_DSA == (int) PUBKEY_ALGO_DSA); log_assert ((int) GCRY_PK_ECC == (int) PUBKEY_ALGO_ECDH); log_assert ((int) GCRY_PK_ELG == (int) PUBKEY_ALGO_ELGAMAL); log_assert ((int) GCRY_CIPHER_NONE == (int) CIPHER_ALGO_NONE); log_assert ((int) GCRY_CIPHER_IDEA == (int) CIPHER_ALGO_IDEA); log_assert ((int) GCRY_CIPHER_3DES == (int) CIPHER_ALGO_3DES); log_assert ((int) GCRY_CIPHER_CAST5 == (int) CIPHER_ALGO_CAST5); log_assert ((int) GCRY_CIPHER_BLOWFISH == (int) CIPHER_ALGO_BLOWFISH); log_assert ((int) GCRY_CIPHER_AES == (int) CIPHER_ALGO_AES); log_assert ((int) GCRY_CIPHER_AES192 == (int) CIPHER_ALGO_AES192); log_assert ((int) GCRY_CIPHER_AES256 == (int) CIPHER_ALGO_AES256); log_assert ((int) GCRY_CIPHER_TWOFISH == (int) CIPHER_ALGO_TWOFISH); log_assert ((int) GCRY_MD_MD5 == (int) DIGEST_ALGO_MD5); log_assert ((int) GCRY_MD_SHA1 == (int) DIGEST_ALGO_SHA1); log_assert ((int) GCRY_MD_RMD160 == (int) DIGEST_ALGO_RMD160); log_assert ((int) GCRY_MD_SHA256 == (int) DIGEST_ALGO_SHA256); log_assert ((int) GCRY_MD_SHA384 == (int) DIGEST_ALGO_SHA384); log_assert ((int) GCRY_MD_SHA512 == (int) DIGEST_ALGO_SHA512); log_assert ((int) GCRY_MD_SHA224 == (int) DIGEST_ALGO_SHA224); switch (gnupg_module_name) { case GNUPG_MODULE_NAME_GPGSM: case GNUPG_MODULE_NAME_GPG: break; default: log_assert (!"no policies for this module"); } module = gnupg_module_name; initialized = 1; } /* Return true if ALGO with a key of KEYLENGTH is compliant to the * given COMPLIANCE mode. If KEY is not NULL, various bits of * information will be extracted from it. If CURVENAME is not NULL, it * is assumed to be the already computed. ALGO may be either an * OpenPGP-style pubkey_algo_t, or a gcrypt-style enum gcry_pk_algos, * both are compatible from the point of view of this function. */ int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, unsigned int algo_flags, gcry_mpi_t key[], unsigned int keylength, const char *curvename) { enum { is_rsa, is_dsa, is_elg, is_ecc } algotype; int result = 0; if (! initialized) return 0; switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: algotype = is_rsa; break; case PUBKEY_ALGO_DSA: algotype = is_dsa; break; case PUBKEY_ALGO_ELGAMAL_E: algotype = is_elg; break; case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: algotype = is_ecc; break; case PUBKEY_ALGO_ELGAMAL: return 0; /* Signing with Elgamal is not at all supported. */ default: /* Unknown. */ return 0; } if (compliance == CO_DE_VS) { char *curve = NULL; switch (algotype) { case is_elg: result = 0; break; case is_rsa: result = (keylength == 2048 || keylength == 3072 || keylength == 4096); /* Although rsaPSS was not part of the original evaluation * we got word that we can claim compliance. */ (void)algo_flags; break; case is_dsa: if (key) { size_t P = gcry_mpi_get_nbits (key[0]); size_t Q = gcry_mpi_get_nbits (key[1]); result = (Q == 256 && (P == 2048 || P == 3072)); } break; case is_ecc: if (!curvename && key) { curve = openpgp_oid_to_str (key[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; } result = (curvename && (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) && (!strcmp (curvename, "brainpoolP256r1") || !strcmp (curvename, "brainpoolP384r1") || !strcmp (curvename, "brainpoolP512r1"))); break; default: result = 0; } xfree (curve); } else { result = 1; /* Assume compliance. */ } return result; } /* Return true if ALGO with the given KEYLENGTH is allowed in the * given COMPLIANCE mode. USE specifies for which use case the * predicate is evaluated. This way policies can be strict in what * they produce, and liberal in what they accept. */ int gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, enum pk_use_case use, int algo, unsigned int algo_flags, gcry_mpi_t key[], unsigned int keylength, const char *curvename) { int result = 0; if (! initialized) return 1; switch (compliance) { case CO_DE_VS: switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: switch (use) { case PK_USE_DECRYPTION: case PK_USE_VERIFICATION: result = 1; break; case PK_USE_ENCRYPTION: case PK_USE_SIGNING: result = (keylength == 2048 || keylength == 3072 || keylength == 4096); break; default: log_assert (!"reached"); } (void)algo_flags; break; case PUBKEY_ALGO_DSA: if (use == PK_USE_VERIFICATION) result = 1; else if (use == PK_USE_SIGNING && key) { size_t P = gcry_mpi_get_nbits (key[0]); size_t Q = gcry_mpi_get_nbits (key[1]); result = (Q == 256 && (P == 2048 || P == 3072)); } break; case PUBKEY_ALGO_ELGAMAL: case PUBKEY_ALGO_ELGAMAL_E: result = (use == PK_USE_DECRYPTION); break; case PUBKEY_ALGO_ECDH: if (use == PK_USE_DECRYPTION) result = 1; else if (use == PK_USE_ENCRYPTION) { char *curve = NULL; if (!curvename && key) { curve = openpgp_oid_to_str (key[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; } result = (curvename && (!strcmp (curvename, "brainpoolP256r1") || !strcmp (curvename, "brainpoolP384r1") || !strcmp (curvename, "brainpoolP512r1"))); xfree (curve); } break; case PUBKEY_ALGO_ECDSA: if (use == PK_USE_VERIFICATION) result = 1; else { char *curve = NULL; if (! curvename && key) { curve = openpgp_oid_to_str (key[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; } result = (use == PK_USE_SIGNING && curvename && (!strcmp (curvename, "brainpoolP256r1") || !strcmp (curvename, "brainpoolP384r1") || !strcmp (curvename, "brainpoolP512r1"))); xfree (curve); } break; case PUBKEY_ALGO_EDDSA: break; default: break; } break; default: /* The default policy is to allow all algorithms. */ result = 1; } return result; } /* Return true if (CIPHER, MODE) is compliant to the given COMPLIANCE mode. */ int gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, cipher_algo_t cipher, enum gcry_cipher_modes mode) { if (! initialized) return 0; switch (compliance) { case CO_DE_VS: switch (cipher) { case CIPHER_ALGO_AES: case CIPHER_ALGO_AES192: case CIPHER_ALGO_AES256: case CIPHER_ALGO_3DES: switch (module) { case GNUPG_MODULE_NAME_GPG: return mode == GCRY_CIPHER_MODE_CFB; case GNUPG_MODULE_NAME_GPGSM: return mode == GCRY_CIPHER_MODE_CBC; } log_assert (!"reached"); default: return 0; } log_assert (!"reached"); default: return 0; } log_assert (!"reached"); } /* Return true if CIPHER is allowed in the given COMPLIANCE mode. If * PRODUCER is true, the predicate is evaluated for the producer, if * false for the consumer. This way policies can be strict in what * they produce, and liberal in what they accept. */ int gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, cipher_algo_t cipher, enum gcry_cipher_modes mode) { if (! initialized) return 1; switch (compliance) { case CO_DE_VS: switch (cipher) { case CIPHER_ALGO_AES: case CIPHER_ALGO_AES192: case CIPHER_ALGO_AES256: case CIPHER_ALGO_3DES: switch (module) { case GNUPG_MODULE_NAME_GPG: return (mode == GCRY_CIPHER_MODE_NONE || mode == GCRY_CIPHER_MODE_CFB); case GNUPG_MODULE_NAME_GPGSM: return (mode == GCRY_CIPHER_MODE_NONE || mode == GCRY_CIPHER_MODE_CBC); } log_assert (!"reached"); case CIPHER_ALGO_BLOWFISH: case CIPHER_ALGO_CAMELLIA128: case CIPHER_ALGO_CAMELLIA192: case CIPHER_ALGO_CAMELLIA256: case CIPHER_ALGO_CAST5: case CIPHER_ALGO_IDEA: case CIPHER_ALGO_TWOFISH: return (module == GNUPG_MODULE_NAME_GPG && (mode == GCRY_CIPHER_MODE_NONE || mode == GCRY_CIPHER_MODE_CFB) && ! producer); default: return 0; } log_assert (!"reached"); default: /* The default policy is to allow all algorithms. */ return 1; } log_assert (!"reached"); } /* Return true if DIGEST is compliant to the given COMPLIANCE mode. */ int gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, digest_algo_t digest) { if (! initialized) return 0; switch (compliance) { case CO_DE_VS: switch (digest) { case DIGEST_ALGO_SHA256: case DIGEST_ALGO_SHA384: case DIGEST_ALGO_SHA512: return 1; default: return 0; } log_assert (!"reached"); default: return 0; } log_assert (!"reached"); } /* Return true if DIGEST is allowed in the given COMPLIANCE mode. If * PRODUCER is true, the predicate is evaluated for the producer, if * false for the consumer. This way policies can be strict in what * they produce, and liberal in what they accept. */ int gnupg_digest_is_allowed (enum gnupg_compliance_mode compliance, int producer, digest_algo_t digest) { if (! initialized) return 1; switch (compliance) { case CO_DE_VS: switch (digest) { case DIGEST_ALGO_SHA256: case DIGEST_ALGO_SHA384: case DIGEST_ALGO_SHA512: return 1; case DIGEST_ALGO_SHA1: case DIGEST_ALGO_SHA224: case DIGEST_ALGO_RMD160: return ! producer; case DIGEST_ALGO_MD5: return ! producer && module == GNUPG_MODULE_NAME_GPGSM; default: return 0; } log_assert (!"reached"); default: /* The default policy is to allow all algorithms. */ return 1; } log_assert (!"reached"); } /* Return True if the random number generator is compliant in * COMPLIANCE mode. */ int gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance) { static int result = -1; if (result != -1) ; /* Use cached result. */ else if (compliance == CO_DE_VS) { - /* In DE_VS mode under Windows we require that the JENT RNG - * is active. */ + /* We also check whether the library is at all compliant. */ + result = gnupg_gcrypt_is_compliant (compliance); + + /* In DE_VS mode under Windows we also require that the JENT RNG + * is active. Check it here. */ #ifdef HAVE_W32_SYSTEM - char *buf; - const char *fields[5]; - - buf = gcry_get_config (0, "rng-type"); - if (buf - && split_fields_colon (buf, fields, DIM (fields)) >= 5 - && atoi (fields[4]) > 0) - result = 1; + if (result == 1) + { + char *buf; + const char *fields[5]; + + buf = gcry_get_config (0, "rng-type"); + if (buf + && split_fields_colon (buf, fields, DIM (fields)) >= 5 + && atoi (fields[4]) > 0) + ; /* Field 5 > 0 := Jent is active. */ + else + result = 0; /* Force non-compliance. */ + gcry_free (buf); + } +#endif /*HAVE_W32_SYSTEM*/ + } + else + result = 1; + + return result; +} + + +/* Return true if the used Libgcrypt is compliant in COMPLIANCE + * mode. */ +int +gnupg_gcrypt_is_compliant (enum gnupg_compliance_mode compliance) +{ + static int result = -1; + + if (result != -1) + ; /* Use cached result. */ + else if (compliance == CO_DE_VS) + { + int is19orlater = !!gcry_check_version ("1.9.0"); + + /* A compliant version of GnuPG requires Libgcrypt >= 1.8.1 and + * less than 1.9.0. Version 1.9.0 requires a re-evaluation and + * can thus not be used for de-vs. */ + if (gcry_check_version ("1.8.1") && !is19orlater) + result = 1; /* Compliant version of Libgcrypt. */ + else if (is19orlater) + { + /* Libgcrypt might be nice enough to tell us whether it is + * compliant. */ + char *buf; + const char *fields[3]; + + buf = gcry_get_config (0, "compliance"); + if (buf + && split_fields_colon (buf, fields, DIM (fields)) >= 2 + && strstr (fields[1], "de-vs")) + result = 1; /* Compliant. */ + else + result = 0; /* Non-compliant. */ + gcry_free (buf); + } else - result = 0; - gcry_free (buf); -#else /*!HAVE_W32_SYSTEM*/ - result = 1; /* Not Windows - RNG is good. */ -#endif /*!HAVE_W32_SYSTEM*/ + result = 0; /* Non-compliant version of Libgcrypt. */ } else result = 1; return result; } const char * gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance) { switch (compliance) { case CO_GNUPG: return "8"; case CO_RFC4880: case CO_RFC2440: case CO_PGP7: case CO_PGP8: log_assert (!"no status code assigned for this compliance mode"); case CO_DE_VS: return "23"; } log_assert (!"invalid compliance mode"); } /* Parse the value of --compliance. Returns the value corresponding * to the given STRING according to OPTIONS of size LENGTH, or -1 * indicating that the lookup was unsuccessful, or the list of options * was printed. If quiet is false, an additional hint to use 'help' * is printed on unsuccessful lookups. */ int gnupg_parse_compliance_option (const char *string, struct gnupg_compliance_option options[], size_t length, int quiet) { size_t i; if (! ascii_strcasecmp (string, "help")) { log_info (_("valid values for option '%s':\n"), "--compliance"); for (i = 0; i < length; i++) log_info (" %s\n", options[i].keyword); return -1; } for (i = 0; i < length; i++) if (! ascii_strcasecmp (string, options[i].keyword)) return options[i].value; log_error (_("invalid value for option '%s'\n"), "--compliance"); if (! quiet) log_info (_("(use \"help\" to list choices)\n")); return -1; } /* Return the command line option for the given COMPLIANCE mode. */ const char * gnupg_compliance_option_string (enum gnupg_compliance_mode compliance) { switch (compliance) { case CO_GNUPG: return "--compliance=gnupg"; case CO_RFC4880: return "--compliance=openpgp"; case CO_RFC2440: return "--compliance=rfc2440"; case CO_PGP7: return "--compliance=pgp7"; case CO_PGP8: return "--compliance=pgp8"; case CO_DE_VS: return "--compliance=de-vs"; } log_assert (!"invalid compliance mode"); } diff --git a/common/compliance.h b/common/compliance.h index 7c74da38a..2f7039206 100644 --- a/common/compliance.h +++ b/common/compliance.h @@ -1,94 +1,95 @@ /* compliance.h - Definitions for compliance modi * Copyright (C) 2017 g10 Code GmbH * Copyright (C) 2017 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file 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 . */ #ifndef GNUPG_COMMON_COMPLIANCE_H #define GNUPG_COMMON_COMPLIANCE_H #include #include "openpgpdefs.h" void gnupg_initialize_compliance (int gnupg_module_name); enum gnupg_compliance_mode { CO_GNUPG, CO_RFC4880, CO_RFC2440, CO_PGP7, CO_PGP8, CO_DE_VS }; enum pk_use_case { PK_USE_ENCRYPTION, PK_USE_DECRYPTION, PK_USE_SIGNING, PK_USE_VERIFICATION, }; /* Flags to distinguish public key algorithm variants. */ #define PK_ALGO_FLAG_RSAPSS 1 /* Use rsaPSS padding. */ int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, unsigned int algo_flags, gcry_mpi_t key[], unsigned int keylength, const char *curvename); int gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, enum pk_use_case use, int algo, unsigned int algo_flags, gcry_mpi_t key[], unsigned int keylength, const char *curvename); int gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, cipher_algo_t cipher, enum gcry_cipher_modes mode); int gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, cipher_algo_t cipher, enum gcry_cipher_modes mode); int gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, digest_algo_t digest); int gnupg_digest_is_allowed (enum gnupg_compliance_mode compliance, int producer, digest_algo_t digest); int gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance); +int gnupg_gcrypt_is_compliant (enum gnupg_compliance_mode compliance); const char *gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance); struct gnupg_compliance_option { const char *keyword; int value; }; int gnupg_parse_compliance_option (const char *string, struct gnupg_compliance_option options[], size_t length, int quiet); const char *gnupg_compliance_option_string (enum gnupg_compliance_mode compliance); #endif /*GNUPG_COMMON_COMPLIANCE_H*/ diff --git a/g10/mainproc.c b/g10/mainproc.c index 08986a070..ca6c24323 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1,2829 +1,2831 @@ /* 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 /* * 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. */ int 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. */ int seen_pkt_encrypted_aead; /* PKT_ENCRYPTED_AEAD 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]); xfree (c->pkenc_list); c->pkenc_list = tmp; } c->pkenc_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; 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, 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"); 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: 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; if (enc->data[0]) { x->data[0] = mpi_copy (enc->data[0]); x->data[1] = mpi_copy (enc->data[1]); } 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; if (pkt->pkttype == PKT_ENCRYPTED_AEAD) c->seen_pkt_encrypted_aead = 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 && list->result != -1) { 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, &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 () /* Symmetric encryption and asymmetric encryption voids compliance. */ && (c->symkeys != !!c->pkenc_list ) /* 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; 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__); /* Now check that every 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) != 0 || ! 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) write_status_strings (STATUS_DECRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS), NULL); } if (!result) result = decrypt_data (c->ctrl, c, pkt->pkt.encrypted, c->dek ); /* Trigger the deferred error. */ if (!result && early_plaintext) result = gpg_error (GPG_ERR_BAD_DATA); 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); else if (pkt->pkt.encrypted->mdc_method && !result) write_status (STATUS_GOODMDC); 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) && 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). */ } 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++; } static int have_seen_pkt_encrypted_aead( CTX c ) { CTX cc; for (cc = c; cc; cc = cc->anchor) { if (cc->seen_pkt_encrypted_aead) 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(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 != -1) 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 = -1; 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, int 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 != -1); 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; } /* Helper for pka_uri_from_sig to parse the to-be-verified address out of the notation data. */ static pka_info_t * get_pka_address (PKT_signature *sig) { pka_info_t *pka = NULL; struct notation *nd,*notation; notation=sig_to_notation(sig); for(nd=notation;nd;nd=nd->next) { if(strcmp(nd->name,"pka-address@gnupg.org")!=0) continue; /* Not the notation we want. */ /* For now we only use the first valid PKA notation. In future we might want to keep additional PKA notations in a linked list. */ if (is_valid_mailbox (nd->value)) { pka = xmalloc (sizeof *pka + strlen(nd->value)); pka->valid = 0; pka->checked = 0; pka->uri = NULL; strcpy (pka->email, nd->value); break; } } free_notation(notation); return pka; } /* Return the URI from a DNS PKA record. If this record has already be retrieved for the signature we merely return it; if not we go out and try to get that DNS record. */ static const char * pka_uri_from_sig (CTX c, PKT_signature *sig) { if (!sig->flags.pka_tried) { log_assert (!sig->pka_info); sig->flags.pka_tried = 1; sig->pka_info = get_pka_address (sig); if (sig->pka_info) { char *url; unsigned char *fpr; size_t fprlen; if (!gpg_dirmngr_get_pka (c->ctrl, sig->pka_info->email, &fpr, &fprlen, &url)) { if (fpr && fprlen == sizeof sig->pka_info->fpr) { memcpy (sig->pka_info->fpr, fpr, fprlen); if (url) { sig->pka_info->valid = 1; if (!*url) xfree (url); else sig->pka_info->uri = url; url = NULL; } } xfree (fpr); xfree (url); } } } return sig->pka_info? sig->pka_info->uri : NULL; } /* 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; 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, 1); 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, 1, 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 avove methods didn't work, our next try is to use the URI * from a DNS PKA record. This is a legacy method which will * eventually be removed. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && (opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) && (opt.keyserver_options.options & KEYSERVER_HONOR_PKA_RECORD)) { const char *uri = pka_uri_from_sig (c, sig); if (uri) { /* FIXME: We might want to locate the key using the fingerprint instead of the keyid. */ int res; struct keyserver_spec *spec; spec = parse_keyserver_uri (uri, 1); if (spec) { if (DBG_LOOKUP) log_debug ("trying auto-key-retrieve method %s\n", "PKA"); free_public_key (pk); pk = NULL; glo_ctrl.in_auto_key_retrieve++; res = keyserver_import_keyid (c->ctrl, sig->keyid, spec, 1); glo_ctrl.in_auto_key_retrieve--; free_keyserver_spec (spec); 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", "PKA", 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, 1); 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); } /* For good signatures print the VALIDSIG status line. */ if (!rc && is_status_enabled () && 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); } /* 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) { if ((opt.verify_options & VERIFY_PKA_LOOKUPS)) pka_uri_from_sig (c, sig); /* Make sure PKA info is available. */ 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) { char pkstrbuf[PUBKEY_STRING_SIZE]; if (pk) pubkey_string (pk, pkstrbuf, sizeof pkstrbuf); else *pkstrbuf = 0; 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); } xfree (dfile); } } /* Compute compliance with CO_DE_VS. */ if (pk && is_status_enabled () + && 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); free_public_key (pk); pk = NULL; release_kbnode( keyblock ); if (rc) g10_errors_seen = 1; if (opt.batch && rc) g10_exit (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 != -1) 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, c->mfx.md2, 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));) check_sig_and_print (c, n1); } 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));) check_sig_and_print (c, n1); } 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 != -1) 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))) check_sig_and_print (c, n1); } 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/sm/decrypt.c b/sm/decrypt.c index 2d846335e..aa91b370d 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1,1035 +1,1035 @@ /* decrypt.c - Decrypt a message * Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/tlv.h" #include "../common/compliance.h" struct decrypt_filter_parm_s { int algo; int mode; int blklen; gcry_cipher_hd_t hd; char iv[16]; size_t ivlen; int any_data; /* did we push anything through the filter at all? */ unsigned char lastblock[16]; /* to strip the padding we have to keep this one */ char helpblock[16]; /* needed because there is no block buffering in libgcrypt (yet) */ int helpblocklen; }; /* Return the hash algorithm's algo id from its name given in the * non-null termnated string in (buffer,buflen). Returns 0 on failure * or if the algo is not known. */ static char * string_from_gcry_buffer (gcry_buffer_t *buffer) { char *string; string = xtrymalloc (buffer->len + 1); if (!string) return NULL; memcpy (string, buffer->data, buffer->len); string[buffer->len] = 0; return string; } /* Helper to construct and hash the * ECC-CMS-SharedInfo ::= SEQUENCE { * keyInfo AlgorithmIdentifier, * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, * suppPubInfo [2] EXPLICIT OCTET STRING } * as described in RFC-5753, 7.2. */ static gpg_error_t hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str, unsigned int keylen, const void *ukm, unsigned int ukmlen) { gpg_error_t err; void *p; unsigned char *oid; size_t n, oidlen, toidlen, tkeyinfo, tukmlen, tsupppubinfo; unsigned char keylenbuf[6]; membuf_t mb = MEMBUF_ZERO; err = ksba_oid_from_str (wrap_algo_str, &oid, &oidlen); if (err) return err; toidlen = get_tlv_length (CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen); tkeyinfo = get_tlv_length (CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen); tukmlen = ukm? get_tlv_length (CLASS_CONTEXT, 0, 1, ukmlen) : 0; keylen *= 8; keylenbuf[0] = TAG_OCTET_STRING; keylenbuf[1] = 4; keylenbuf[2] = (keylen >> 24); keylenbuf[3] = (keylen >> 16); keylenbuf[4] = (keylen >> 8); keylenbuf[5] = keylen; tsupppubinfo = get_tlv_length (CLASS_CONTEXT, 2, 1, sizeof keylenbuf); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1, tkeyinfo + tukmlen + tsupppubinfo); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen); put_membuf (&mb, oid, oidlen); ksba_free (oid); if (ukm) { put_tlv_to_membuf (&mb, CLASS_CONTEXT, 0, 1, ukmlen); put_membuf (&mb, ukm, ukmlen); } put_tlv_to_membuf (&mb, CLASS_CONTEXT, 2, 1, sizeof keylenbuf); put_membuf (&mb, keylenbuf, sizeof keylenbuf); p = get_membuf (&mb, &n); if (!p) return gpg_error_from_syserror (); gcry_md_write (hash_hd, p, n); xfree (p); return 0; } /* Derive a KEK (key wrapping key) using (SECRET,SECRETLEN), an * optional (UKM,ULMLEN), the wrap algorithm WRAP_ALGO_STR in decimal * dotted form, and the hash algorithm HASH_ALGO. On success a key of * length KEYLEN is stored at KEY. */ gpg_error_t ecdh_derive_kek (unsigned char *key, unsigned int keylen, int hash_algo, const char *wrap_algo_str, const void *secret, unsigned int secretlen, const void *ukm, unsigned int ukmlen) { gpg_error_t err = 0; unsigned int hashlen; gcry_md_hd_t hash_hd; unsigned char counter; unsigned int n, ncopy; hashlen = gcry_md_get_algo_dlen (hash_algo); if (!hashlen) return gpg_error (GPG_ERR_INV_ARG); err = gcry_md_open (&hash_hd, hash_algo, 0); if (err) return err; /* According to SEC1 3.6.1 we should check that * SECRETLEN + UKMLEN + 4 < maxhashlen * However, we have no practical limit on the hash length and thus * there is no point in checking this. The second check that * KEYLEN < hashlen*(2^32-1) * is obviously also not needed. */ for (n=0, counter=1; n < keylen; counter++) { if (counter > 1) gcry_md_reset (hash_hd); gcry_md_write (hash_hd, secret, secretlen); gcry_md_write (hash_hd, "\x00\x00\x00", 3); /* MSBs of counter */ gcry_md_write (hash_hd, &counter, 1); err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, ukm, ukmlen); if (err) break; gcry_md_final (hash_hd); if (n + hashlen > keylen) ncopy = keylen - n; else ncopy = hashlen; memcpy (key+n, gcry_md_read (hash_hd, 0), ncopy); n += ncopy; } gcry_md_close (hash_hd); return err; } /* This function will modify SECRET. NBITS is the size of the curve * which which we took from the certificate. */ static gpg_error_t ecdh_decrypt (unsigned char *secret, size_t secretlen, unsigned int nbits, gcry_sexp_t enc_val, unsigned char **r_result, unsigned int *r_resultlen) { gpg_error_t err; gcry_buffer_t ioarray[4] = { {0}, {0}, {0}, {0} }; char *encr_algo_str = NULL; char *wrap_algo_str = NULL; int hash_algo, cipher_algo; const unsigned char *ukm; /* Alias for ioarray[2]. */ unsigned int ukmlen; const unsigned char *data; /* Alias for ioarray[3]. */ unsigned int datalen; unsigned int keylen; unsigned char key[32]; gcry_cipher_hd_t cipher_hd = NULL; unsigned char *result = NULL; unsigned int resultlen; *r_resultlen = 0; *r_result = NULL; /* Extract X from SECRET; this is the actual secret. Unless a * smartcard diretcly returns X, it must be in the format of: * * 04 || X || Y * 40 || X * 41 || X */ if (secretlen < 2) return gpg_error (GPG_ERR_BAD_DATA); if (secretlen == (nbits+7)/8) ; /* Matches curve length - this is already the X coordinate. */ else if (*secret == 0x04) { secretlen--; memmove (secret, secret+1, secretlen); if ((secretlen & 1)) return gpg_error (GPG_ERR_BAD_DATA); secretlen /= 2; } else if (*secret == 0x40 || *secret == 0x41) { secretlen--; memmove (secret, secret+1, secretlen); } else return gpg_error (GPG_ERR_BAD_DATA); if (!secretlen) return gpg_error (GPG_ERR_BAD_DATA); if (DBG_CRYPTO) log_printhex (secret, secretlen, "ECDH X ..:"); /* We have now the shared secret bytes in (SECRET,SECRETLEN). Now * we will compute the KEK using a value dervied from the secret * bytes. */ err = gcry_sexp_extract_param (enc_val, "enc-val", "&'encr-algo''wrap-algo''ukm'?s", ioarray+0, ioarray+1, ioarray+2, ioarray+3, NULL); if (err) { log_error ("extracting ECDH parameter failed: %s\n", gpg_strerror (err)); goto leave; } encr_algo_str = string_from_gcry_buffer (ioarray); if (!encr_algo_str) { err = gpg_error_from_syserror (); goto leave; } wrap_algo_str = string_from_gcry_buffer (ioarray+1); if (!wrap_algo_str) { err = gpg_error_from_syserror (); goto leave; } ukm = ioarray[2].data; ukmlen = ioarray[2].len; data = ioarray[3].data; datalen = ioarray[3].len; /* Check parameters. */ if (DBG_CRYPTO) { log_debug ("encr_algo: %s\n", encr_algo_str); log_debug ("wrap_algo: %s\n", wrap_algo_str); log_printhex (ukm, ukmlen, "ukm .....:"); log_printhex (data, datalen, "data ....:"); } if (!strcmp (encr_algo_str, "1.3.132.1.11.1")) { /* dhSinglePass-stdDH-sha256kdf-scheme */ hash_algo = GCRY_MD_SHA256; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.2")) { /* dhSinglePass-stdDH-sha384kdf-scheme */ hash_algo = GCRY_MD_SHA384; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.3")) { /* dhSinglePass-stdDH-sha512kdf-scheme */ hash_algo = GCRY_MD_SHA512; } else if (!strcmp (encr_algo_str, "1.3.133.16.840.63.0.2")) { /* dhSinglePass-stdDH-sha1kdf-scheme */ hash_algo = GCRY_MD_SHA1; } else { err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.5")) { cipher_algo = GCRY_CIPHER_AES128; keylen = 16; } else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.25")) { cipher_algo = GCRY_CIPHER_AES192; keylen = 24; } else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.45")) { cipher_algo = GCRY_CIPHER_AES256; keylen = 32; } else { err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, secret, secretlen, ukm, ukmlen); if (err) goto leave; if (DBG_CRYPTO) log_printhex (key, keylen, "KEK .....:"); /* Unwrap the key. */ if ((datalen % 8) || datalen < 16) { log_error ("can't use a shared secret of %u bytes for ecdh\n", datalen); err = gpg_error (GPG_ERR_BAD_DATA); goto leave; } resultlen = datalen - 8; result = xtrymalloc_secure (resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) { log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_setkey (cipher_hd, key, keylen); wipememory (key, sizeof key); if (err) { log_error ("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_decrypt (cipher_hd, result, resultlen, data, datalen); if (err) { log_error ("ecdh failed in gcry_cipher_decrypt: %s\n",gpg_strerror (err)); goto leave; } *r_resultlen = resultlen; *r_result = result; result = NULL; leave: if (result) { wipememory (result, resultlen); xfree (result); } gcry_cipher_close (cipher_hd); xfree (encr_algo_str); xfree (wrap_algo_str); xfree (ioarray[0].data); xfree (ioarray[1].data); xfree (ioarray[2].data); xfree (ioarray[3].data); return err; } /* Decrypt the session key and fill in the parm structure. The algo and the IV is expected to be already in PARM. */ static int prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, int pk_algo, unsigned int nbits, const char *desc, ksba_const_sexp_t enc_val, struct decrypt_filter_parm_s *parm) { char *seskey = NULL; size_t n, seskeylen; int rc; if (DBG_CRYPTO) log_printcanon ("decrypting:", enc_val, 0); rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val, &seskey, &seskeylen); if (rc) { log_error ("error decrypting session key: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_CRYPTO) log_printhex (seskey, seskeylen, "DEK frame:"); n=0; if (pk_algo == GCRY_PK_ECC) { gcry_sexp_t s_enc_val; unsigned char *decrypted; unsigned int decryptedlen; rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val, gcry_sexp_canon_len (enc_val, 0, NULL, NULL)); if (rc) goto leave; rc = ecdh_decrypt (seskey, seskeylen, nbits, s_enc_val, &decrypted, &decryptedlen); gcry_sexp_release (s_enc_val); if (rc) goto leave; xfree (seskey); seskey = decrypted; seskeylen = decryptedlen; } else if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16) { /* Smells like an AES-128, 3-DES, or AES-256 key. This might * happen because a SC has already done the unpacking. A better * solution would be to test for this only after we triggered * the GPG_ERR_INV_SESSION_KEY. */ } else { if (n + 7 > seskeylen ) { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } /* FIXME: Actually the leading zero is required but due to the way we encode the output in libgcrypt as an MPI we are not able to encode that leading zero. However, when using a Smartcard we are doing it the right way and therefore we have to skip the zero. This should be fixed in gpg-agent of course. */ if (!seskey[n]) n++; if (seskey[n] != 2 ) /* Wrong block type version. */ { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */ ; n++; /* and the zero byte */ if (n >= seskeylen ) { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } } if (DBG_CRYPTO) log_printhex (seskey+n, seskeylen-n, "CEK .....:"); if (opt.verbose) log_info (_("%s.%s encrypted data\n"), gcry_cipher_algo_name (parm->algo), cipher_mode_to_string (parm->mode)); rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0); if (rc) { log_error ("error creating decryptor: %s\n", gpg_strerror (rc)); goto leave; } rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n); if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY) { log_info (_("WARNING: message was encrypted with " "a weak key in the symmetric cipher.\n")); rc = 0; } if (rc) { log_error("key setup failed: %s\n", gpg_strerror(rc) ); goto leave; } gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen); leave: xfree (seskey); return rc; } /* This function is called by the KSBA writer just before the actual write is done. The function must take INLEN bytes from INBUF, decrypt it and store it inoutbuf which has a maximum size of maxoutlen. The valid bytes in outbuf should be return in outlen. Due to different buffer sizes or different length of input and output, it may happen that fewer bytes are processed or fewer bytes are written. */ static gpg_error_t decrypt_filter (void *arg, const void *inbuf, size_t inlen, size_t *inused, void *outbuf, size_t maxoutlen, size_t *outlen) { struct decrypt_filter_parm_s *parm = arg; int blklen = parm->blklen; size_t orig_inlen = inlen; /* fixme: Should we issue an error when we have not seen one full block? */ if (!inlen) return gpg_error (GPG_ERR_BUG); if (maxoutlen < 2*parm->blklen) return gpg_error (GPG_ERR_BUG); /* Make some space because we will later need an extra block at the end. */ maxoutlen -= blklen; if (parm->helpblocklen) { int i, j; for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++) parm->helpblock[i] = ((const char*)inbuf)[j]; inlen -= j; if (blklen > maxoutlen) return gpg_error (GPG_ERR_BUG); if (i < blklen) { parm->helpblocklen = i; *outlen = 0; } else { parm->helpblocklen = 0; if (parm->any_data) { memcpy (outbuf, parm->lastblock, blklen); *outlen =blklen; } else *outlen = 0; gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen, parm->helpblock, blklen); parm->any_data = 1; } *inused = orig_inlen - inlen; return 0; } if (inlen > maxoutlen) inlen = maxoutlen; if (inlen % blklen) { /* store the remainder away */ parm->helpblocklen = inlen%blklen; inlen = inlen/blklen*blklen; memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen); } *inused = inlen + parm->helpblocklen; if (inlen) { log_assert (inlen >= blklen); if (parm->any_data) { gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen, inbuf, inlen); memcpy (outbuf, parm->lastblock, blklen); memcpy (parm->lastblock,(char*)outbuf+inlen, blklen); *outlen = inlen; } else { gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen); memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen); *outlen = inlen - blklen; parm->any_data = 1; } } else *outlen = 0; return 0; } /* Perform a decrypt operation. */ int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) { int rc; gnupg_ksba_io_t b64reader = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_reader_t reader; ksba_writer_t writer; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh; int recp; estream_t in_fp = NULL; struct decrypt_filter_parm_s dfparm; memset (&dfparm, 0, sizeof dfparm); audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT); kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } in_fp = es_fdopen_nc (in_fd, "rb"); if (!in_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)), in_fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, writer); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_SETUP_READY); /* Parser loop. */ do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA || stopreason == KSBA_SR_DETACHED_DATA) { int algo, mode; const char *algoid; int any_key = 0; int is_de_vs; /* Computed compliance with CO_DE_VS. */ audit_log (ctrl->audit, AUDIT_GOT_DATA); algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/); algo = gcry_cipher_map_name (algoid); mode = gcry_cipher_mode_from_oid (algoid); if (!algo || !mode) { rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); log_error ("unsupported algorithm '%s'\n", algoid? algoid:"?"); if (algoid && !strcmp (algoid, "1.2.840.113549.3.2")) log_info (_("(this is the RC2 algorithm)\n")); else if (!algoid) log_info (_("(this does not seem to be an encrypted" " message)\n")); { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm", numbuf, algoid?algoid:"?", NULL); audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid); } /* If it seems that this is not an encrypted message we return a more sensible error code. */ if (!algoid) rc = gpg_error (GPG_ERR_NO_DATA); goto leave; } /* Check compliance. */ if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode)) { log_error (_("cipher algorithm '%s'" " may not be used in %s mode\n"), gcry_cipher_algo_name (algo), gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } /* For CMS, CO_DE_VS demands CBC mode. */ is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode); audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo); dfparm.algo = algo; dfparm.mode = mode; dfparm.blklen = gcry_cipher_get_algo_blklen (algo); if (dfparm.blklen > sizeof (dfparm.helpblock)) return gpg_error (GPG_ERR_BUG); rc = ksba_cms_get_content_enc_iv (cms, dfparm.iv, sizeof (dfparm.iv), &dfparm.ivlen); if (rc) { log_error ("error getting IV: %s\n", gpg_strerror (rc)); goto leave; } for (recp=0; !any_key; recp++) { char *issuer; ksba_sexp_t serial; ksba_sexp_t enc_val; char *hexkeygrip = NULL; char *pkalgostr = NULL; char *pkfpr = NULL; char *desc = NULL; char kidbuf[16+1]; int tmp_rc; ksba_cert_t cert = NULL; unsigned int nbits; int pk_algo = 0; *kidbuf = 0; tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); if (tmp_rc == -1 && recp) break; /* no more recipients */ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); if (tmp_rc) log_error ("recp %d - error getting info: %s\n", recp, gpg_strerror (tmp_rc)); else { if (opt.verbose) { log_info ("recp %d - issuer: '%s'\n", recp, issuer? issuer:"[NONE]"); log_info ("recp %d - serial: ", recp); gpgsm_dump_serial (serial); log_printf ("\n"); } if (ctrl->audit) { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr); xfree (tmpstr); } keydb_search_reset (kh); rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial); if (rc) { log_error ("failed to find the certificate: %s\n", gpg_strerror(rc)); goto oops; } rc = keydb_get_cert (kh, &cert); if (rc) { log_error ("failed to get cert: %s\n", gpg_strerror (rc)); goto oops; } /* Print the ENC_TO status line. Note that we can do so only if we have the certificate. This is in contrast to gpg where the keyID is commonly included in the encrypted messages. It is too cumbersome to retrieve the used algorithm, thus we don't print it for now. We also record the keyid for later use. */ { unsigned long kid[2]; kid[0] = gpgsm_get_short_fingerprint (cert, kid+1); snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX", kid[1], kid[0]); gpgsm_status2 (ctrl, STATUS_ENC_TO, kidbuf, "0", "0", NULL); } /* Put the certificate into the audit log. */ audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0); /* Just in case there is a problem with the own certificate we print this message - should never happen of course */ rc = gpgsm_cert_use_decrypt_p (cert); if (rc) { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage", numbuf, NULL); rc = 0; } hexkeygrip = gpgsm_get_keygrip_hexstring (cert); desc = gpgsm_format_keydesc (cert); pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); pk_algo = gpgsm_get_key_algo_info (cert, &nbits); if (!opt.quiet) log_info (_("encrypted to %s key %s\n"), pkalgostr, pkfpr); /* Check compliance. */ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cert, NULL)); log_info (_("key %s is not suitable for decryption" " in %s mode\n"), kidstr, gnupg_compliance_option_string(opt.compliance)); rc = gpg_error (GPG_ERR_PUBKEY_ALGO); goto oops; } /* Check that all certs are compliant with CO_DE_VS. */ is_de_vs = (is_de_vs && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL)); oops: if (rc) { /* We cannot check compliance of certs that we * don't have. */ is_de_vs = 0; } xfree (issuer); xfree (serial); ksba_cert_release (cert); } if (!hexkeygrip || !pk_algo) ; else if (!(enc_val = ksba_cms_get_enc_val (cms, recp))) log_error ("recp %d - error getting encrypted session key\n", recp); else { rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits, desc, enc_val, &dfparm); xfree (enc_val); if (rc) { log_info ("decrypting session key failed: %s\n", gpg_strerror (rc)); if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf) gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL); } else { /* setup the bulk decrypter */ any_key = 1; ksba_writer_set_filter (writer, decrypt_filter, &dfparm); - if (is_de_vs) + if (is_de_vs && gnupg_gcrypt_is_compliant (CO_DE_VS)) gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); } audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc); } xfree (pkalgostr); xfree (pkfpr); xfree (hexkeygrip); xfree (desc); } /* If we write an audit log add the unused recipients to the log as well. */ if (ctrl->audit && any_key) { for (;; recp++) { char *issuer; ksba_sexp_t serial; int tmp_rc; tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); if (tmp_rc == -1) break; /* no more recipients */ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); if (tmp_rc) log_error ("recp %d - error getting info: %s\n", recp, gpg_strerror (tmp_rc)); else { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr); xfree (tmpstr); xfree (issuer); xfree (serial); } } } if (!any_key) { if (!rc) rc = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } } else if (stopreason == KSBA_SR_END_DATA) { ksba_writer_set_filter (writer, NULL, NULL); if (dfparm.any_data) { /* write the last block with padding removed */ int i, npadding = dfparm.lastblock[dfparm.blklen-1]; if (!npadding || npadding > dfparm.blklen) { log_error ("invalid padding with value %d\n", npadding); rc = gpg_error (GPG_ERR_INV_DATA); goto leave; } rc = ksba_writer_write (writer, dfparm.lastblock, dfparm.blklen - npadding); if (rc) goto leave; for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++) { if (dfparm.lastblock[i] != npadding) { log_error ("inconsistent padding\n"); rc = gpg_error (GPG_ERR_INV_DATA); goto leave; } } } } } while (stopreason != KSBA_SR_READY); rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL); leave: audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc); if (rc) { gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL); log_error ("message decryption failed: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc)); } ksba_cms_release (cms); gnupg_ksba_destroy_reader (b64reader); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); es_fclose (in_fp); if (dfparm.hd) gcry_cipher_close (dfparm.hd); return rc; } diff --git a/sm/encrypt.c b/sm/encrypt.c index 34a5e878b..fbd88b6cd 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -1,854 +1,854 @@ /* encrypt.c - Encrypt a message * Copyright (C) 2001, 2003, 2004, 2007, 2008, * 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/compliance.h" struct dek_s { const char *algoid; int algo; gcry_cipher_hd_t chd; char key[32]; int keylen; char iv[32]; int ivlen; }; typedef struct dek_s *DEK; /* Callback parameters for the encryption. */ struct encrypt_cb_parm_s { estream_t fp; DEK dek; int eof_seen; int ready; int readerror; int bufsize; unsigned char *buffer; int buflen; }; /* Initialize the data encryption key (session key). */ static int init_dek (DEK dek) { int rc=0, mode, i; dek->algo = gcry_cipher_map_name (dek->algoid); mode = gcry_cipher_mode_from_oid (dek->algoid); if (!dek->algo || !mode) { log_error ("unsupported algorithm '%s'\n", dek->algoid); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } /* Extra check for algorithms we consider to be too weak for encryption, although we support them for decryption. Note that there is another check below discriminating on the key length. */ switch (dek->algo) { case GCRY_CIPHER_DES: case GCRY_CIPHER_RFC2268_40: log_error ("cipher algorithm '%s' not allowed: too weak\n", gnupg_cipher_algo_name (dek->algo)); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); default: break; } dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); if (!dek->keylen || dek->keylen > sizeof (dek->key)) return gpg_error (GPG_ERR_BUG); dek->ivlen = gcry_cipher_get_algo_blklen (dek->algo); if (!dek->ivlen || dek->ivlen > sizeof (dek->iv)) return gpg_error (GPG_ERR_BUG); /* Make sure we don't use weak keys. */ if (dek->keylen < 100/8) { log_error ("key length of '%s' too small\n", dek->algoid); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } rc = gcry_cipher_open (&dek->chd, dek->algo, mode, GCRY_CIPHER_SECURE); if (rc) { log_error ("failed to create cipher context: %s\n", gpg_strerror (rc)); return rc; } for (i=0; i < 8; i++) { gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); rc = gcry_cipher_setkey (dek->chd, dek->key, dek->keylen); if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY) break; log_info(_("weak key created - retrying\n") ); } if (rc) { log_error ("failed to set the key: %s\n", gpg_strerror (rc)); gcry_cipher_close (dek->chd); dek->chd = NULL; return rc; } gcry_create_nonce (dek->iv, dek->ivlen); rc = gcry_cipher_setiv (dek->chd, dek->iv, dek->ivlen); if (rc) { log_error ("failed to set the IV: %s\n", gpg_strerror (rc)); gcry_cipher_close (dek->chd); dek->chd = NULL; return rc; } return 0; } /* Encode an RSA session key. */ static int encode_session_key (DEK dek, gcry_sexp_t * r_data) { gcry_sexp_t data; char *p; int rc; p = xtrymalloc (64 + 2 * dek->keylen); if (!p) return gpg_error_from_syserror (); strcpy (p, "(data\n (flags pkcs1)\n (value #"); bin2hex (dek->key, dek->keylen, p + strlen (p)); strcat (p, "#))\n"); rc = gcry_sexp_sscan (&data, NULL, p, strlen (p)); xfree (p); *r_data = data; return rc; } /* Encrypt DEK using ECDH. S_PKEY is the public key. On success the * result is stored at R_ENCVAL. Example of a public key: * * (public-key (ecc (curve "1.3.132.0.34") (q #04B0[...]B8#))) * */ static gpg_error_t ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) { gpg_error_t err; gcry_sexp_t l1; char *curvebuf = NULL; const char *curve; unsigned int curvebits; const char *encr_algo_str; const char *wrap_algo_str; int hash_algo, cipher_algo; unsigned int keylen; unsigned char key[32]; gcry_sexp_t s_data = NULL; gcry_sexp_t s_encr = NULL; gcry_buffer_t ioarray[2] = { {0}, {0} }; unsigned char *secret; /* Alias for ioarray[0]. */ unsigned int secretlen; unsigned char *pubkey; /* Alias for ioarray[1]. */ unsigned int pubkeylen; gcry_cipher_hd_t cipher_hd = NULL; unsigned char *result = NULL; unsigned int resultlen; *r_encval = NULL; /* Figure out the encryption and wrap algo OIDs. */ /* Get the curve name if any, */ l1 = gcry_sexp_find_token (s_pkey, "curve", 0); if (l1) { curvebuf = gcry_sexp_nth_string (l1, 1); gcry_sexp_release (l1); } if (!curvebuf) { err = gpg_error (GPG_ERR_INV_CURVE); log_error ("%s: invalid public key: no curve\n", __func__); goto leave; } /* We need to use our OpenPGP mapping to turn a curve name into its * canonical numerical OID. We also use this to get the size of the * curve which we need to figure out a suitable hash algo. We * should have a Libgcrypt function to do this; see bug report #4926. */ curve = openpgp_curve_to_oid (curvebuf, &curvebits, NULL); if (!curve) { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); log_error ("%s: invalid public key: %s\n", __func__, gpg_strerror (err)); goto leave; } xfree (curvebuf); curvebuf = NULL; /* Our mapping matches the recommended algorithms from RFC-5753 but * not supporting the short curves which would require 3DES. */ if (curvebits < 255) { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); log_error ("%s: curve '%s' is not supported\n", __func__, curve); goto leave; } else if (opt.force_ecdh_sha1kdf) { /* dhSinglePass-stdDH-sha1kdf-scheme */ encr_algo_str = "1.3.133.16.840.63.0.2"; wrap_algo_str = "2.16.840.1.101.3.4.1.45"; hash_algo = GCRY_MD_SHA1; cipher_algo = GCRY_CIPHER_AES256; keylen = 32; } else if (curvebits <= 256) { /* dhSinglePass-stdDH-sha256kdf-scheme */ encr_algo_str = "1.3.132.1.11.1"; wrap_algo_str = "2.16.840.1.101.3.4.1.5"; hash_algo = GCRY_MD_SHA256; cipher_algo = GCRY_CIPHER_AES128; keylen = 16; } else if (curvebits <= 384) { /* dhSinglePass-stdDH-sha384kdf-scheme */ encr_algo_str = "1.3.132.1.11.2"; wrap_algo_str = "2.16.840.1.101.3.4.1.25"; hash_algo = GCRY_MD_SHA384; cipher_algo = GCRY_CIPHER_AES256; keylen = 24; } else { /* dhSinglePass-stdDH-sha512kdf-scheme*/ encr_algo_str = "1.3.132.1.11.3"; wrap_algo_str = "2.16.840.1.101.3.4.1.45"; hash_algo = GCRY_MD_SHA512; cipher_algo = GCRY_CIPHER_AES256; keylen = 32; } /* Create a secret and an ephemeral key. */ { char *k; k = gcry_random_bytes_secure ((curvebits+7)/8, GCRY_STRONG_RANDOM); if (DBG_CRYPTO) log_printhex (k, (curvebits+7)/8, "ephm. k .:"); err = gcry_sexp_build (&s_data, NULL, "%b", (int)(curvebits+7)/8, k); xfree (k); } if (err) { log_error ("%s: error building ephemeral secret: %s\n", __func__, gpg_strerror (err)); goto leave; } err = gcry_pk_encrypt (&s_encr, s_data, s_pkey); if (err) { log_error ("%s: error encrypting ephemeral secret: %s\n", __func__, gpg_strerror (err)); goto leave; } err = gcry_sexp_extract_param (s_encr, NULL, "&se", &ioarray+0, ioarray+1, NULL); if (err) { log_error ("%s: error extracting ephemeral key and secret: %s\n", __func__, gpg_strerror (err)); goto leave; } secret = ioarray[0].data; secretlen = ioarray[0].len; pubkey = ioarray[1].data; pubkeylen = ioarray[1].len; if (DBG_CRYPTO) { log_printhex (pubkey, pubkeylen, "pubkey ..:"); log_printhex (secret, secretlen, "secret ..:"); } /* Extract X coordinate from SECRET. */ if (secretlen < 5) /* 5 because N could be reduced to (n-1)/2. */ err = gpg_error (GPG_ERR_BAD_DATA); else if (*secret == 0x04) { secretlen--; memmove (secret, secret+1, secretlen); if ((secretlen & 1)) { err = gpg_error (GPG_ERR_BAD_DATA); goto leave; } secretlen /= 2; } else if (*secret == 0x40 || *secret == 0x41) { secretlen--; memmove (secret, secret+1, secretlen); } else err = gpg_error (GPG_ERR_BAD_DATA); if (err) goto leave; if (DBG_CRYPTO) log_printhex (secret, secretlen, "ECDH X ..:"); err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, secret, secretlen, NULL, 0); if (err) goto leave; if (DBG_CRYPTO) log_printhex (key, keylen, "KEK .....:"); /* Wrap the key. */ if ((dek->keylen % 8) || dek->keylen < 16) { log_error ("%s: can't use a session key of %u bytes\n", __func__, dek->keylen); err = gpg_error (GPG_ERR_BAD_DATA); goto leave; } resultlen = dek->keylen + 8; result = xtrymalloc_secure (resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) { log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, gpg_strerror (err)); goto leave; } err = gcry_cipher_setkey (cipher_hd, key, keylen); wipememory (key, sizeof key); if (err) { log_error ("%s: failed in gcry_cipher_setkey: %s\n", __func__, gpg_strerror (err)); goto leave; } err = gcry_cipher_encrypt (cipher_hd, result, resultlen, dek->key, dek->keylen); if (err) { log_error ("%s: failed in gcry_cipher_encrypt: %s\n", __func__, gpg_strerror (err)); goto leave; } if (DBG_CRYPTO) log_printhex (result, resultlen, "w(CEK) ..:"); err = gcry_sexp_build (r_encval, NULL, "(enc-val(ecdh(e%b)(s%b)(encr-algo%s)(wrap-algo%s)))", (int)pubkeylen, pubkey, (int)resultlen, result, encr_algo_str, wrap_algo_str, NULL); if (err) log_error ("%s: failed building final S-exp: %s\n", __func__, gpg_strerror (err)); leave: gcry_cipher_close (cipher_hd); wipememory (key, sizeof key); xfree (result); xfree (ioarray[0].data); xfree (ioarray[1].data); gcry_sexp_release (s_data); gcry_sexp_release (s_encr); xfree (curvebuf); return err; } /* Encrypt the DEK under the key contained in CERT and return it as a * canonical S-expressions at ENCVAL. PK_ALGO is the public key * algorithm which the caller has already retrieved from CERT. */ static int encrypt_dek (const DEK dek, ksba_cert_t cert, int pk_algo, unsigned char **encval) { gcry_sexp_t s_ciph, s_data, s_pkey; int rc; ksba_sexp_t buf; size_t len; *encval = NULL; /* get the key from the cert */ buf = ksba_cert_get_public_key (cert); if (!buf) { log_error ("no public key for recipient\n"); return gpg_error (GPG_ERR_NO_PUBKEY); } len = gcry_sexp_canon_len (buf, 0, NULL, NULL); if (!len) { log_error ("libksba did not return a proper S-Exp\n"); return gpg_error (GPG_ERR_BUG); } rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)buf, len); xfree (buf); buf = NULL; if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); return rc; } if (DBG_CRYPTO) { log_printsexp (" pubkey:", s_pkey); log_printhex (dek->key, dek->keylen, "CEK .....:"); } /* Put the encoded cleartext into a simple list. */ s_data = NULL; /* (avoid compiler warning) */ if (pk_algo == GCRY_PK_ECC) { rc = ecdh_encrypt (dek, s_pkey, &s_ciph); } else { rc = encode_session_key (dek, &s_data); if (rc) { log_error ("encode_session_key failed: %s\n", gpg_strerror (rc)); return rc; } if (DBG_CRYPTO) log_printsexp (" data:", s_data); /* pass it to libgcrypt */ rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); } gcry_sexp_release (s_data); gcry_sexp_release (s_pkey); if (DBG_CRYPTO) log_printsexp ("enc-val:", s_ciph); /* Reformat it. */ if (!rc) { rc = make_canon_sexp (s_ciph, encval, NULL); gcry_sexp_release (s_ciph); } return rc; } /* do the actual encryption */ static int encrypt_cb (void *cb_value, char *buffer, size_t count, size_t *nread) { struct encrypt_cb_parm_s *parm = cb_value; int blklen = parm->dek->ivlen; unsigned char *p; size_t n; *nread = 0; if (!buffer) return -1; /* not supported */ if (parm->ready) return -1; if (count < blklen) BUG (); if (!parm->eof_seen) { /* fillup the buffer */ p = parm->buffer; for (n=parm->buflen; n < parm->bufsize; n++) { int c = es_getc (parm->fp); if (c == EOF) { if (es_ferror (parm->fp)) { parm->readerror = errno; return -1; } parm->eof_seen = 1; break; } p[n] = c; } parm->buflen = n; } n = parm->buflen < count? parm->buflen : count; n = n/blklen * blklen; if (n) { /* encrypt the stuff */ gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n); *nread = n; /* Who cares about cycles, take the easy way and shift the buffer */ parm->buflen -= n; memmove (parm->buffer, parm->buffer+n, parm->buflen); } else if (parm->eof_seen) { /* no complete block but eof: add padding */ /* fixme: we should try to do this also in the above code path */ int i, npad = blklen - (parm->buflen % blklen); p = parm->buffer; for (n=parm->buflen, i=0; n < parm->bufsize && i < npad; n++, i++) p[n] = npad; gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n); *nread = n; parm->ready = 1; } return 0; } /* Perform an encrypt operation. Encrypt the data received on DATA-FD and write it to OUT_FP. The recipients are take from the certificate given in recplist; if this is NULL it will be encrypted for a default recipient */ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) { int rc = 0; gnupg_ksba_io_t b64writer = NULL; gpg_error_t err; ksba_writer_t writer; ksba_reader_t reader = NULL; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; struct encrypt_cb_parm_s encparm; DEK dek = NULL; int recpno; estream_t data_fp = NULL; certlist_t cl; int count; int compliant; memset (&encparm, 0, sizeof encparm); audit_set_type (ctrl->audit, AUDIT_TYPE_ENCRYPT); /* Check that the certificate list is not empty and that at least one certificate is not flagged as encrypt_to; i.e. is a real recipient. */ for (cl = recplist; cl; cl = cl->next) if (!cl->is_encrypt_to) break; if (!cl) { log_error(_("no valid recipients given\n")); gpgsm_status (ctrl, STATUS_NO_RECP, "0"); audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0); rc = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } for (count = 0, cl = recplist; cl; cl = cl->next) count++; audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, count); kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Fixme: We should use the unlocked version of the es functions. */ data_fp = es_fdopen_nc (data_fd, "rb"); if (!data_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } err = ksba_reader_new (&reader); if (err) rc = err; if (!rc) rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm); if (rc) goto leave; encparm.fp = data_fp; ctrl->pem_name = "ENCRYPTED MESSAGE"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_new (&cms); if (err) { rc = err; goto leave; } err = ksba_cms_set_reader_writer (cms, reader, writer); if (err) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } audit_log (ctrl->audit, AUDIT_GOT_DATA); /* We are going to create enveloped data with uninterpreted data as inner content */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA); if (!err) err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); if (err) { log_error ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* Check compliance. */ if (!gnupg_cipher_is_allowed (opt.compliance, 1, gcry_cipher_map_name (opt.def_cipher_algoid), gcry_cipher_mode_from_oid (opt.def_cipher_algoid))) { log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), opt.def_cipher_algoid, gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "random-compliance", rc); goto leave; } /* Create a session key */ dek = xtrycalloc_secure (1, sizeof *dek); if (!dek) rc = out_of_core (); else { dek->algoid = opt.def_cipher_algoid; rc = init_dek (dek); } if (rc) { log_error ("failed to create the session key: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen); if (err) { log_error ("ksba_cms_set_content_enc_algo failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } encparm.dek = dek; /* Use a ~8k (AES) or ~4k (3DES) buffer */ encparm.bufsize = 500 * dek->ivlen; encparm.buffer = xtrymalloc (encparm.bufsize); if (!encparm.buffer) { rc = out_of_core (); goto leave; } audit_log_s (ctrl->audit, AUDIT_SESSION_KEY, dek->algoid); compliant = gnupg_cipher_is_compliant (CO_DE_VS, dek->algo, GCRY_CIPHER_MODE_CBC); /* Gather certificates of recipients, encrypt the session key for each and store them in the CMS object */ for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next) { unsigned char *encval; unsigned int nbits; int pk_algo; /* Check compliance. */ pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); if (!gnupg_pk_is_compliant (opt.compliance, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cl->cert, NULL)); log_info (_("WARNING: key %s is not suitable for encryption" " in %s mode\n"), kidstr, gnupg_compliance_option_string (opt.compliance)); } /* Fixme: When adding ECC we need to provide the curvename and * the key to gnupg_pk_is_compliant. */ if (compliant && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL)) compliant = 0; rc = encrypt_dek (dek, cl->cert, pk_algo, &encval); if (rc) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc); log_error ("encryption failed for recipient no. %d: %s\n", recpno, gpg_strerror (rc)); goto leave; } err = ksba_cms_add_recipient (cms, cl->cert); if (err) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("ksba_cms_add_recipient failed: %s\n", gpg_strerror (err)); rc = err; xfree (encval); goto leave; } err = ksba_cms_set_enc_val (cms, recpno, encval); xfree (encval); audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); if (err) { log_error ("ksba_cms_set_enc_val failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } - if (compliant) + if (compliant && gnupg_gcrypt_is_compliant (CO_DE_VS)) gpgsm_status (ctrl, STATUS_ENCRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); /* Main control loop for encryption. */ recpno = 0; do { err = ksba_cms_build (cms, &stopreason); if (err) { log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } while (stopreason != KSBA_SR_READY); if (encparm.readerror) { log_error ("error reading input: %s\n", strerror (encparm.readerror)); rc = gpg_error (gpg_err_code_from_errno (encparm.readerror)); goto leave; } rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE); log_info ("encrypted data created\n"); leave: ksba_cms_release (cms); gnupg_ksba_destroy_writer (b64writer); ksba_reader_release (reader); keydb_release (kh); xfree (dek); es_fclose (data_fp); xfree (encparm.buffer); return rc; } diff --git a/sm/verify.c b/sm/verify.c index 1575a1eb2..fe111c32a 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -1,733 +1,734 @@ /* verify.c - Verify a messages signature * Copyright (C) 2001, 2002, 2003, 2007, * 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/compliance.h" static char * strtimestamp_r (ksba_isotime_t atime) { char *buffer = xmalloc (15); if (!atime || !*atime) strcpy (buffer, "none"); else sprintf (buffer, "%.4s-%.2s-%.2s", atime, atime+4, atime+6); return buffer; } /* Hash the data for a detached signature. Returns 0 on success. */ static gpg_error_t hash_data (int fd, gcry_md_hd_t md) { gpg_error_t err = 0; estream_t fp; char buffer[4096]; int nread; fp = es_fdopen_nc (fd, "rb"); if (!fp) { err = gpg_error_from_syserror (); log_error ("fdopen(%d) failed: %s\n", fd, gpg_strerror (err)); return err; } do { nread = es_fread (buffer, 1, DIM(buffer), fp); gcry_md_write (md, buffer, nread); } while (nread); if (es_ferror (fp)) { err = gpg_error_from_syserror (); log_error ("read error on fd %d: %s\n", fd, gpg_strerror (err)); } es_fclose (fp); return err; } /* Perform a verify operation. To verify detached signatures, DATA_FD must be different than -1. With OUT_FP given and a non-detached signature, the signed material is written to that stream. */ int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) { int i, rc; gnupg_ksba_io_t b64reader = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_reader_t reader; ksba_writer_t writer = NULL; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; ksba_cert_t cert; KEYDB_HANDLE kh; gcry_md_hd_t data_md = NULL; int signer; const char *algoid; int algo; int is_detached; estream_t in_fp = NULL; char *p; audit_set_type (ctrl->audit, AUDIT_TYPE_VERIFY); kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } in_fp = es_fdopen_nc (in_fd, "rb"); if (!in_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)), in_fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } if (out_fp) { rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } } rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, writer); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } rc = gcry_md_open (&data_md, 0, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (data_md, "vrfy.data"); audit_log (ctrl->audit, AUDIT_SETUP_READY); is_detached = 0; do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_NEED_HASH) { is_detached = 1; audit_log (ctrl->audit, AUDIT_DETACHED_SIGNATURE); if (opt.verbose) log_info ("detached signature\n"); } if (stopreason == KSBA_SR_NEED_HASH || stopreason == KSBA_SR_BEGIN_DATA) { audit_log (ctrl->audit, AUDIT_GOT_DATA); /* We are now able to enable the hash algorithms */ for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++) { algo = gcry_md_map_name (algoid); if (!algo) { log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?"); if (algoid && ( !strcmp (algoid, "1.2.840.113549.1.1.2") ||!strcmp (algoid, "1.2.840.113549.2.2"))) log_info (_("(this is the MD2 algorithm)\n")); audit_log_s (ctrl->audit, AUDIT_BAD_DATA_HASH_ALGO, algoid); } else { if (DBG_X509) log_debug ("enabling hash algorithm %d (%s)\n", algo, algoid? algoid:""); gcry_md_enable (data_md, algo); audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); } } if (opt.extra_digest_algo) { if (DBG_X509) log_debug ("enabling extra hash algorithm %d\n", opt.extra_digest_algo); gcry_md_enable (data_md, opt.extra_digest_algo); audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, opt.extra_digest_algo); } if (is_detached) { if (data_fd == -1) { log_info ("detached signature w/o data " "- assuming certs-only\n"); audit_log (ctrl->audit, AUDIT_CERT_ONLY_SIG); } else audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING, hash_data (data_fd, data_md)); } else { ksba_cms_set_hash_function (cms, HASH_FNC, data_md); } } else if (stopreason == KSBA_SR_END_DATA) { /* The data bas been hashed */ audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING, 0); } } while (stopreason != KSBA_SR_READY); if (b64writer) { rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); audit_log_ok (ctrl->audit, AUDIT_WRITE_ERROR, rc); goto leave; } } if (data_fd != -1 && !is_detached) { log_error ("data given for a non-detached signature\n"); rc = gpg_error (GPG_ERR_CONFLICT); audit_log (ctrl->audit, AUDIT_USAGE_ERROR); goto leave; } for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) { /* Fixme: it might be better to check the validity of the certificate first before entering it into the DB. This way we would avoid cluttering the DB with invalid certificates. */ audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, keydb_store_cert (ctrl, cert, 0, NULL)); ksba_cert_release (cert); } cert = NULL; for (signer=0; ; signer++) { char *issuer = NULL; gcry_sexp_t sigval = NULL; ksba_isotime_t sigtime, keyexptime; ksba_sexp_t serial; char *msgdigest = NULL; size_t msgdigestlen; char *ctattr; int sigval_hash_algo; int info_pkalgo; unsigned int nbits; int pkalgo; char *pkalgostr = NULL; char *pkfpr = NULL; unsigned int pkalgoflags, verifyflags; rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial); if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA && data_fd == -1 && is_detached) { log_info ("certs-only message accepted\n"); rc = 0; break; } if (rc) { if (signer && rc == -1) rc = 0; break; } gpgsm_status (ctrl, STATUS_NEWSIG, NULL); audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer); if (DBG_X509) { log_debug ("signer %d - issuer: '%s'\n", signer, issuer? issuer:"[NONE]"); log_debug ("signer %d - serial: ", signer); gpgsm_dump_serial (serial); log_printf ("\n"); } if (ctrl->audit) { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_SIG_NAME, tmpstr); xfree (tmpstr); } rc = ksba_cms_get_signing_time (cms, signer, sigtime); if (gpg_err_code (rc) == GPG_ERR_NO_DATA) *sigtime = 0; else if (rc) { log_error ("error getting signing time: %s\n", gpg_strerror (rc)); *sigtime = 0; /* (we can't encode an error in the time string.) */ } rc = ksba_cms_get_message_digest (cms, signer, &msgdigest, &msgdigestlen); if (!rc) { algoid = ksba_cms_get_digest_algo (cms, signer); algo = gcry_md_map_name (algoid); if (DBG_X509) log_debug ("signer %d - digest algo: %d\n", signer, algo); if (! gcry_md_is_enabled (data_md, algo)) { log_error ("digest algo %d (%s) has not been enabled\n", algo, algoid?algoid:""); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "unsupported"); goto next_signer; } } else if (gpg_err_code (rc) == GPG_ERR_NO_DATA) { log_assert (!msgdigest); rc = 0; algoid = NULL; algo = 0; } else /* real error */ { audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); break; } rc = ksba_cms_get_sigattr_oids (cms, signer, "1.2.840.113549.1.9.3", &ctattr); if (!rc) { const char *s; if (DBG_X509) log_debug ("signer %d - content-type attribute: %s", signer, ctattr); s = ksba_cms_get_content_oid (cms, 1); if (!s || strcmp (ctattr, s)) { log_error ("content-type attribute does not match " "actual content-type\n"); ksba_free (ctattr); ctattr = NULL; audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } ksba_free (ctattr); ctattr = NULL; } else if (rc != -1) { log_error ("error getting content-type attribute: %s\n", gpg_strerror (rc)); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } rc = 0; sigval = gpgsm_ksba_cms_get_sig_val (cms, signer); if (!sigval) { log_error ("no signature value available\n"); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } sigval_hash_algo = gpgsm_get_hash_algo_from_sigval (sigval, &pkalgoflags); if (DBG_X509) { log_debug ("signer %d - signature available (sigval hash=%d pkaf=%u)", signer, sigval_hash_algo, pkalgoflags); } if (!sigval_hash_algo) sigval_hash_algo = algo; /* Fallback used e.g. with old libksba. */ /* Find the certificate of the signer */ keydb_search_reset (kh); rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial); if (rc) { if (rc == -1) { log_error ("certificate not found\n"); rc = gpg_error (GPG_ERR_NO_PUBKEY); } else log_error ("failed to find the certificate: %s\n", gpg_strerror(rc)); { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "verify.findkey", numbuf, NULL); } audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "no-cert"); goto next_signer; } rc = keydb_get_cert (kh, &cert); if (rc) { log_error ("failed to get cert: %s\n", gpg_strerror (rc)); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); goto next_signer; } pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); pkalgo = gpgsm_get_key_algo_info (cert, &nbits); /* Remap the ECC algo to the algo we use. Note that EdDSA has * already been mapped. */ if (pkalgo == GCRY_PK_ECC) pkalgo = GCRY_PK_ECDSA; /* Print infos about the signature. */ log_info (_("Signature made ")); if (*sigtime) { /* We take the freedom as noted in RFC3339 to use a space * instead of the "T" delimiter between date and time. We * also append a separate UTC instead of a "Z" or "+00:00" * suffix because that makes it clear to everyone what kind * of time this is. */ dump_isotime (sigtime); log_printf (" UTC"); } else log_printf (_("[date not given]")); log_info (_(" using %s key %s\n"), pkalgostr, pkfpr); if (opt.verbose) { log_info (_("algorithm:")); log_printf (" %s + %s", pubkey_algo_to_string (pkalgo), gcry_md_algo_name (sigval_hash_algo)); if (algo != sigval_hash_algo) log_printf (" (%s)", gcry_md_algo_name (algo)); log_printf ("\n"); } audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); /* Check compliance. */ if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, pkalgo, pkalgoflags, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cert, NULL)); log_error (_("key %s may not be used for signing in %s mode\n"), kidstr, gnupg_compliance_option_string (opt.compliance)); goto next_signer; } if (! gnupg_digest_is_allowed (opt.compliance, 0, sigval_hash_algo)) { log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (sigval_hash_algo), gnupg_compliance_option_string (opt.compliance)); goto next_signer; } /* Check compliance with CO_DE_VS. */ if (gnupg_pk_is_compliant (CO_DE_VS, pkalgo, pkalgoflags, NULL, nbits, NULL) + && gnupg_gcrypt_is_compliant (CO_DE_VS) && gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo)) gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); /* Now we can check the signature. */ if (msgdigest) { /* Signed attributes are available. */ gcry_md_hd_t md; unsigned char *s; /* Check that the message digest in the signed attributes matches the one we calculated on the data. */ s = gcry_md_read (data_md, algo); if ( !s || !msgdigestlen || gcry_md_get_algo_dlen (algo) != msgdigestlen || memcmp (s, msgdigest, msgdigestlen) ) { char *fpr; log_error (_("invalid signature: message digest attribute " "does not match computed one\n")); if (DBG_X509) { if (msgdigest) log_printhex (msgdigest, msgdigestlen, "message: "); if (s) log_printhex (s, gcry_md_get_algo_dlen (algo), "computed: "); } fpr = gpgsm_fpr_and_name_for_status (cert); gpgsm_status (ctrl, STATUS_BADSIG, fpr); xfree (fpr); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO, sigval_hash_algo); rc = gcry_md_open (&md, sigval_hash_algo, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); goto next_signer; } if (DBG_HASHING) gcry_md_debug (md, "vrfy.attr"); ksba_cms_set_hash_function (cms, HASH_FNC, md); rc = ksba_cms_hash_signed_attrs (cms, signer); if (rc) { log_error ("hashing signed attrs failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); goto next_signer; } rc = gpgsm_check_cms_signature (cert, sigval, md, sigval_hash_algo, pkalgoflags, &info_pkalgo); gcry_md_close (md); } else { rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo, pkalgoflags, &info_pkalgo); } if (rc) { char *fpr; log_error ("invalid signature: %s\n", gpg_strerror (rc)); fpr = gpgsm_fpr_and_name_for_status (cert); gpgsm_status (ctrl, STATUS_BADSIG, fpr); xfree (fpr); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } rc = gpgsm_cert_use_verify_p (cert); /*(this displays an info message)*/ if (rc) { gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "verify.keyusage", gpg_err_code (rc)); rc = 0; } if (DBG_X509) log_debug ("signature okay - checking certs\n"); audit_log (ctrl->audit, AUDIT_VALIDATE_CHAIN); rc = gpgsm_validate_chain (ctrl, cert, *sigtime? sigtime : "19700101T000000", keyexptime, 0, NULL, 0, &verifyflags); { char *fpr, *buf, *tstr; fpr = gpgsm_fpr_and_name_for_status (cert); if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED) { gpgsm_status (ctrl, STATUS_EXPKEYSIG, fpr); rc = 0; } else gpgsm_status (ctrl, STATUS_GOODSIG, fpr); xfree (fpr); /* FIXME: INFO_PKALGO correctly shows ECDSA but PKALGO is then * ECC. We should use the ECDSA here and need to find a way to * figure this oult without using the bodus assumtion in * gpgsm_check_cms_signature that ECC is alwas ECDSA. */ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); tstr = strtimestamp_r (sigtime); buf = xasprintf ("%s %s %s %s 0 0 %d %d 00", fpr, tstr, *sigtime? sigtime : "0", *keyexptime? keyexptime : "0", info_pkalgo, algo); xfree (tstr); xfree (fpr); gpgsm_status (ctrl, STATUS_VALIDSIG, buf); xfree (buf); } audit_log_ok (ctrl->audit, AUDIT_CHAIN_STATUS, rc); if (rc) /* of validate_chain */ { log_error ("invalid certification chain: %s\n", gpg_strerror (rc)); if (gpg_err_code (rc) == GPG_ERR_BAD_CERT_CHAIN || gpg_err_code (rc) == GPG_ERR_BAD_CERT || gpg_err_code (rc) == GPG_ERR_BAD_CA_CERT || gpg_err_code (rc) == GPG_ERR_CERT_REVOKED) gpgsm_status_with_err_code (ctrl, STATUS_TRUST_NEVER, NULL, gpg_err_code (rc)); else gpgsm_status_with_err_code (ctrl, STATUS_TRUST_UNDEFINED, NULL, gpg_err_code (rc)); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "good"); for (i=0; (p = ksba_cert_get_subject (cert, i)); i++) { log_info (!i? _("Good signature from") : _(" aka")); log_printf (" \""); gpgsm_es_print_name (log_get_stream (), p); log_printf ("\"\n"); ksba_free (p); } /* Print a note if this is a qualified signature. */ { size_t qualbuflen; char qualbuffer[1]; rc = ksba_cert_get_user_data (cert, "is_qualified", &qualbuffer, sizeof (qualbuffer), &qualbuflen); if (!rc && qualbuflen) { if (*qualbuffer) { log_info (_("This is a qualified signature\n")); if (!opt.qualsig_approval) log_info (_("Note, that this software is not officially approved " "to create or verify such signatures.\n")); } } else if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("get_user_data(is_qualified) failed: %s\n", gpg_strerror (rc)); } gpgsm_status (ctrl, STATUS_TRUST_FULLY, (verifyflags & VALIDATE_FLAG_STEED)? "0 steed": (verifyflags & VALIDATE_FLAG_CHAIN_MODEL)? "0 chain": "0 shell"); next_signer: rc = 0; xfree (issuer); xfree (serial); gcry_sexp_release (sigval); xfree (msgdigest); xfree (pkalgostr); xfree (pkfpr); ksba_cert_release (cert); cert = NULL; } rc = 0; leave: ksba_cms_release (cms); gnupg_ksba_destroy_reader (b64reader); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); gcry_md_close (data_md); es_fclose (in_fp); if (rc) { char numbuf[50]; sprintf (numbuf, "%d", rc ); gpgsm_status2 (ctrl, STATUS_ERROR, "verify.leave", numbuf, NULL); } return rc; }