diff --git a/doc/gpg.texi b/doc/gpg.texi index a70204043..ffd7a976e 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -2789,9 +2789,20 @@ message was tampered with intentionally by an attacker. @item --allow-weak-digest-algos @opindex allow-weak-digest-algos -Signatures made with the broken MD5 algorithm are normally rejected -with an ``invalid digest algorithm'' message. This option allows the -verification of signatures made with such weak algorithms. +Signatures made with known-weak digest algorithms are normally +rejected with an ``invalid digest algorithm'' message. This option +allows the verification of signatures made with such weak algorithms. +MD5 is the only digest algorithm considered weak by default. See also +@option{--weak-digest} to reject other digest algorithms. + +@item --weak-digest @code{name} +@opindex weak-digest +Treat the specified digest algorithm as weak. Signatures made over +weak digests algorithms are normally rejected. This option can be +supplied multiple times if multiple algorithms should be considered +weak. See also @option{--allow-weak-digest-algos} to disable +rejection of weak digests. MD5 is always considered weak, and does +not need to be listed explicitly. @item --no-default-keyring @opindex no-default-keyring diff --git a/doc/gpgv.texi b/doc/gpgv.texi index 6bcbc0add..280966bf8 100644 --- a/doc/gpgv.texi +++ b/doc/gpgv.texi @@ -118,6 +118,14 @@ checks into warnings. @include opt-homedir.texi +@item --weak-digest @code{name} +@opindex weak-digest +Treat the specified digest algorithm as weak. Signatures made over +weak digests algorithms are normally rejected. This option can be +supplied multiple times if multiple algorithms should be considered +weak. MD5 is always considered weak, and does not need to be listed +explicitly. + @end table @mansect return value diff --git a/g10/gpg.c b/g10/gpg.c index ada913c0a..cb610a11b 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -389,6 +389,7 @@ enum cmd_and_opt_values oPrintDANERecords, oTOFUDefaultPolicy, oTOFUDBFormat, + oWeakDigest, oNoop }; @@ -749,6 +750,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oPersonalCompressPreferences, "personal-compress-preferences", "@"), ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), + ARGPARSE_s_s (oWeakDigest, "weak-digest","@"), /* Aliases. I constantly mistype these, and assume other people do as well. */ @@ -2208,6 +2210,7 @@ main (int argc, char **argv) set_homedir (default_homedir ()); opt.passphrase_repeat = 1; opt.emit_version = 1; /* Limit to the major number. */ + opt.additional_weak_digests = NULL; /* Check whether we have a config file on the command line. */ orig_argc = argc; @@ -3124,6 +3127,9 @@ main (int argc, char **argv) break; case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break; + case oWeakDigest: + additional_weak_digest(pargs.r.ret_str); + break; case oDisplay: set_opt_session_env ("DISPLAY", pargs.r.ret_str); diff --git a/g10/gpgv.c b/g10/gpgv.c index 0807622a6..23e761047 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -61,6 +61,7 @@ enum cmd_and_opt_values { oStatusFD, oLoggerFD, oHomedir, + oWeakDigest, aTest }; @@ -78,6 +79,7 @@ static ARGPARSE_OPTS opts[] = { N_("|FD|write status info to this FD")), ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), ARGPARSE_s_s (oHomedir, "homedir", "@"), + ARGPARSE_s_s (oWeakDigest, "weak-digest", "@"), ARGPARSE_end () }; @@ -192,6 +194,9 @@ main( int argc, char **argv ) log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1)); break; case oHomedir: opt.homedir = pargs.r.ret_str; break; + case oWeakDigest: + additional_weak_digest(pargs.r.ret_str); + break; case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break; default : pargs.err = ARGPARSE_PRINT_ERROR; break; } diff --git a/g10/main.h b/g10/main.h index c9521ad1d..0226c6418 100644 --- a/g10/main.h +++ b/g10/main.h @@ -69,6 +69,12 @@ struct groupitem struct groupitem *next; }; +struct weakhash +{ + enum gcry_md_algos algo; + struct weakhash *next; +}; + /*-- gpg.c --*/ extern int g10_errors_seen; @@ -82,6 +88,7 @@ void print_pubkey_algo_note (pubkey_algo_t algo); void print_cipher_algo_note (cipher_algo_t algo); void print_digest_algo_note (digest_algo_t algo); void print_md5_rejected_note (void); +void additional_weak_digest (const char* digestname); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); diff --git a/g10/misc.c b/g10/misc.c index 9134b2823..c135059d8 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -307,6 +307,10 @@ print_cipher_algo_note (cipher_algo_t algo) void print_digest_algo_note (digest_algo_t algo) { + int deprecated = 0; + const enum gcry_md_algos galgo = map_md_openpgp_to_gcry (algo); + const struct weakhash *weak; + if(algo >= 100 && algo <= 110) { static int warn=0; @@ -315,14 +319,21 @@ print_digest_algo_note (digest_algo_t algo) warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental digest algorithm %s\n"), - gcry_md_algo_name (algo)); + gcry_md_algo_name (galgo)); } } - else if(algo==DIGEST_ALGO_MD5) + else if(algo == DIGEST_ALGO_MD5) + deprecated = 1; + else + for (weak = opt.additional_weak_digests; weak != NULL; weak = weak->next) + if (weak->algo == galgo) + deprecated = 1; + + if (deprecated) { es_fflush (es_stdout); log_info (_("WARNING: digest algorithm %s is deprecated\n"), - gcry_md_algo_name (algo)); + gcry_md_algo_name (galgo)); } } @@ -1676,3 +1687,37 @@ ecdsa_qbits_from_Q (unsigned int qbits) qbits /= 2; return qbits; } + + +/* Ignore signatures and certifications made over certain digest + * algorithms by default, MD5 is considered weak. This allows users + * to deprecate support for other algorithms as well. + */ +void +additional_weak_digest (const char* digestname) +{ + struct weakhash *weak = NULL; + const enum gcry_md_algos algo = string_to_digest_algo(digestname); + + if (algo == GCRY_MD_MD5) + return; /* MD5 is always considered weak, no need to add it. */ + + if (algo == GCRY_MD_NONE) + { + log_error(_("Unknown weak digest '%s'\n"), digestname); + return; + } + + /* Check to ensure it's not already present. */ + for (weak = opt.additional_weak_digests; weak != NULL; weak = weak->next) + { + if (algo == weak->algo) + return; + } + + /* Add it to the head of the list. */ + weak = xmalloc(sizeof(*weak)); + weak->algo = algo; + weak->next = opt.additional_weak_digests; + opt.additional_weak_digests = weak; +} diff --git a/g10/options.h b/g10/options.h index 2135aa0b9..c1ea9ddd8 100644 --- a/g10/options.h +++ b/g10/options.h @@ -170,6 +170,7 @@ struct prefitem_t *personal_cipher_prefs; prefitem_t *personal_digest_prefs; prefitem_t *personal_compress_prefs; + struct weakhash *additional_weak_digests; int no_perm_warn; int no_mdc_warn; char *temp_dir; diff --git a/g10/sig-check.c b/g10/sig-check.c index d45a9f378..84930d615 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -274,15 +274,18 @@ do_check( PKT_public_key *pk, PKT_signature *sig, gcry_md_hd_t digest, { gcry_mpi_t result = NULL; int rc = 0; + const struct weakhash *weak; if( (rc=do_check_messages(pk,sig,r_expired,r_revoked)) ) return rc; - if (sig->digest_algo == GCRY_MD_MD5 - && !opt.flags.allow_weak_digest_algos) + if (!opt.flags.allow_weak_digest_algos) { - print_md5_rejected_note (); - return GPG_ERR_DIGEST_ALGO; + if (sig->digest_algo == GCRY_MD_MD5) + return GPG_ERR_DIGEST_ALGO; + for (weak = opt.additional_weak_digests; weak; weak = weak->next) + if (sig->digest_algo == weak->algo) + return GPG_ERR_DIGEST_ALGO; } /* Make sure the digest algo is enabled (in case of a detached