From f88cb12f8e3c1234a094d09e2505d3a3eec4cbfe Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 30 Aug 2022 18:55:15 +0200 Subject: [PATCH] gpg: Make --require-compliance work for -se * g10/encrypt.c (encrypt_crypt, encrypt_filter): Factor common code out to ... (create_dek_with_warnings): new (check_encryption_compliance): and new. * g10/encrypt.c (encrypt_filter): Add the compliance check. -- GnuPG-bug-id: 6174 --- g10/encrypt.c | 284 ++++++++++++++++++++++++++------------------------ 1 file changed, 145 insertions(+), 139 deletions(-) diff --git a/g10/encrypt.c b/g10/encrypt.c index 9cf8abdd7..4b055fd26 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -1,7 +1,7 @@ /* encrypt.c - Main encryption driver * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006, 2009 Free Software Foundation, Inc. - * Copyright (C) 2016 g10 Code GmbH + * Copyright (C) 2016, 2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -67,6 +67,65 @@ encrypt_store (const char *filename) } +/* Create an setup DEK structure and print approriate warnings. The + * FALLBACK_TO_3DES flag is used to handle the two different ways we + * use this code. PK_LIST gives the list of public keys. Always + * returns a DEK. The actual session needs to be added later. */ +static DEK * +create_dek_with_warnings (int fallback_to_3des, pk_list_t pk_list) +{ + DEK *dek; + + dek = xmalloc_secure_clear (sizeof *dek); + if (!opt.def_cipher_algo) + { + /* Try to get it from the prefs. */ + dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL); + if (dek->algo == -1 && fallback_to_3des) + { + /* The only way select_algo_from_prefs can fail here is when + * mixing v3 and v4 keys, as v4 keys have an implicit + * preference entry for 3DES, and the pk_list cannot be + * empty. In this case, use 3DES anyway as it's the safest + * choice - perhaps the v3 key is being used in an OpenPGP + * implementation and we know that the implementation behind + * any v4 key can handle 3DES. */ + dek->algo = CIPHER_ALGO_3DES; + } + else if (dek->algo == -1) + { + /* Because 3DES is implicitly in the prefs, this can only + * happen if we do not have any public keys in the list. */ + dek->algo = DEFAULT_CIPHER_ALGO; + } + + /* In case 3DES has been selected, print a warning if any key + * does not have a preference for AES. This should help to + * indentify why encrypting to several recipients falls back to + * 3DES. */ + if (opt.verbose && dek->algo == CIPHER_ALGO_3DES) + warn_missing_aes_from_pklist (pk_list); + } + else + { + if (!opt.expert + && (select_algo_from_prefs (pk_list, PREFTYPE_SYM, + opt.def_cipher_algo, NULL) + != opt.def_cipher_algo)) + { + log_info(_("WARNING: forcing symmetric cipher %s (%d)" + " violates recipient preferences\n"), + openpgp_cipher_algo_name (opt.def_cipher_algo), + opt.def_cipher_algo); + } + + dek->algo = opt.def_cipher_algo; + } + + return dek; +} + + /* *SESKEY contains the unencrypted session key ((*SESKEY)->KEY) and the algorithm that will be used to encrypt the contents of the SED packet ((*SESKEY)->ALGO). If *SESKEY is NULL, then a random @@ -212,7 +271,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if ( use_seskey ) { - DEK *dek = NULL; + DEK *dek = NULL; /* Dummy. */ seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ()); encrypt_seskey( cfx.dek, &dek, enckey ); @@ -446,6 +505,80 @@ write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, } +/* Check whether all encryption keys are compliant with the current + * mode and issue respective status lines. DEK has the info about the + * session key and PK_LIST the list of public keys. */ +static gpg_error_t +check_encryption_compliance (DEK *dek, pk_list_t pk_list) +{ + gpg_error_t err = 0; + pk_list_t pkr; + int compliant; + + if (! gnupg_cipher_is_allowed (opt.compliance, 1, dek->algo, + GCRY_CIPHER_MODE_CFB)) + { + log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), + openpgp_cipher_algo_name (dek->algo), + gnupg_compliance_option_string (opt.compliance)); + err = gpg_error (GPG_ERR_CIPHER_ALGO); + goto leave; + } + + if (!gnupg_rng_is_compliant (opt.compliance)) + { + err = gpg_error (GPG_ERR_FORBIDDEN); + log_error (_("%s is not compliant with %s mode\n"), + "RNG", + gnupg_compliance_option_string (opt.compliance)); + write_status_error ("random-compliance", err); + goto leave; + } + + compliant = gnupg_cipher_is_compliant (CO_DE_VS, dek->algo, + GCRY_CIPHER_MODE_CFB); + + for (pkr = pk_list; pkr; pkr = pkr->next) + { + PKT_public_key *pk = pkr->pk; + unsigned int nbits = nbits_from_pk (pk); + + if (!gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0, + pk->pkey, nbits, NULL)) + log_info (_("WARNING: key %s is not suitable for encryption" + " in %s mode\n"), + keystr_from_pk (pk), + gnupg_compliance_option_string (opt.compliance)); + + if (compliant + && !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey, + nbits, NULL)) + compliant = 0; /* Not compliant - reset flag. */ + } + + /* If we are compliant print the status for de-vs compliance. */ + if (compliant) + write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE, + gnupg_status_compliance_flag (CO_DE_VS), + NULL); + + /* Check whether we should fail the operation. */ + if (opt.flags.require_compliance + && opt.compliance == CO_DE_VS + && !compliant) + { + log_error (_("operation forced to fail due to" + " unfulfilled compliance rules\n")); + err = gpg_error (GPG_ERR_FORBIDDEN); + g10_errors_seen = 1; + goto leave; + } + + leave: + return err; +} + + /* * Encrypt the file with the given userids (or ask if none is * supplied). Either FILENAME or FILEFD must be given, but not both. @@ -475,7 +608,6 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, progress_filter_context_t *pfx; PK_LIST pk_list; int do_compress; - int compliant; if (filefd != -1 && filename) return gpg_error (GPG_ERR_INV_ARG); /* Both given. */ @@ -563,109 +695,13 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, push_armor_filter (afx, out); } - /* Create a session key. */ - cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek); - if (!opt.def_cipher_algo) - { - /* Try to get it from the prefs. */ - cfx.dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL); - /* The only way select_algo_from_prefs can fail here is when - mixing v3 and v4 keys, as v4 keys have an implicit preference - entry for 3DES, and the pk_list cannot be empty. In this - case, use 3DES anyway as it's the safest choice - perhaps the - v3 key is being used in an OpenPGP implementation and we know - that the implementation behind any v4 key can handle 3DES. */ - if (cfx.dek->algo == -1) - { - cfx.dek->algo = CIPHER_ALGO_3DES; - } - - /* In case 3DES has been selected, print a warning if any key - does not have a preference for AES. This should help to - indentify why encrypting to several recipients falls back to - 3DES. */ - if (opt.verbose && cfx.dek->algo == CIPHER_ALGO_3DES) - warn_missing_aes_from_pklist (pk_list); - } - else - { - if (!opt.expert - && (select_algo_from_prefs (pk_list, PREFTYPE_SYM, - opt.def_cipher_algo, NULL) - != opt.def_cipher_algo)) - { - log_info(_("WARNING: forcing symmetric cipher %s (%d)" - " violates recipient preferences\n"), - openpgp_cipher_algo_name (opt.def_cipher_algo), - opt.def_cipher_algo); - } - - cfx.dek->algo = opt.def_cipher_algo; - } - - /* Check compliance. */ - if (! gnupg_cipher_is_allowed (opt.compliance, 1, cfx.dek->algo, - GCRY_CIPHER_MODE_CFB)) - { - log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), - openpgp_cipher_algo_name (cfx.dek->algo), - 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)); - write_status_error ("random-compliance", rc); - goto leave; - } - - compliant = gnupg_cipher_is_compliant (CO_DE_VS, cfx.dek->algo, - GCRY_CIPHER_MODE_CFB); - - { - pk_list_t pkr; - - for (pkr = pk_list; pkr; pkr = pkr->next) - { - PKT_public_key *pk = pkr->pk; - unsigned int nbits = nbits_from_pk (pk); - - if (!gnupg_pk_is_compliant (opt.compliance, pk->pubkey_algo, 0, - pk->pkey, nbits, NULL)) - log_info (_("WARNING: key %s is not suitable for encryption" - " in %s mode\n"), - keystr_from_pk (pk), - gnupg_compliance_option_string (opt.compliance)); - - if (compliant - && !gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey, - nbits, NULL)) - compliant = 0; - } - - } - - if (compliant) - write_status_strings (STATUS_ENCRYPTION_COMPLIANCE_MODE, - gnupg_status_compliance_flag (CO_DE_VS), - NULL); - - if (opt.flags.require_compliance - && opt.compliance == CO_DE_VS - && !compliant) - { - log_error (_("operation forced to fail due to" - " unfulfilled compliance rules\n")); - rc = gpg_error (GPG_ERR_FORBIDDEN); - g10_errors_seen = 1; - goto leave; - } + /* Create a session key (a DEK). */ + cfx.dek = create_dek_with_warnings (1, pk_list); + /* Check compliance etc. */ + rc = check_encryption_compliance (cfx.dek, pk_list); + if (rc) + goto leave; cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo); @@ -842,41 +878,11 @@ encrypt_filter (void *opaque, int control, { if ( !efx->header_okay ) { - efx->cfx.dek = xmalloc_secure_clear ( sizeof *efx->cfx.dek ); - if ( !opt.def_cipher_algo ) - { - /* Try to get it from the prefs. */ - efx->cfx.dek->algo = - select_algo_from_prefs (efx->pk_list, PREFTYPE_SYM, -1, NULL); - if (efx->cfx.dek->algo == -1 ) - { - /* Because 3DES is implicitly in the prefs, this can - only happen if we do not have any public keys in - the list. */ - efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO; - } + efx->cfx.dek = create_dek_with_warnings (0, efx->pk_list); - /* In case 3DES has been selected, print a warning if - any key does not have a preference for AES. This - should help to indentify why encrypting to several - recipients falls back to 3DES. */ - if (opt.verbose - && efx->cfx.dek->algo == CIPHER_ALGO_3DES) - warn_missing_aes_from_pklist (efx->pk_list); - } - else - { - if (!opt.expert - && select_algo_from_prefs (efx->pk_list,PREFTYPE_SYM, - opt.def_cipher_algo, - NULL) != opt.def_cipher_algo) - log_info(_("forcing symmetric cipher %s (%d) " - "violates recipient preferences\n"), - openpgp_cipher_algo_name (opt.def_cipher_algo), - opt.def_cipher_algo); - - efx->cfx.dek->algo = opt.def_cipher_algo; - } + rc = check_encryption_compliance (efx->cfx.dek, efx->pk_list); + if (rc) + return rc; efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo);