From a51067a21f688086bd8e44234a88ae367582cc76 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 29 Sep 2022 15:03:26 +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 Ported-from: f88cb12f8e3c1234a094d09e2505d3a3eec4cbfe --- g10/encrypt.c | 300 ++++++++++++++++++++++++-------------------------- 1 file changed, 145 insertions(+), 155 deletions(-) diff --git a/g10/encrypt.c b/g10/encrypt.c index 28a761747..bd98d06e4 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -67,6 +67,143 @@ encrypt_store (const char *filename) } +/* Create and setup a DEK structure and print approriate warnings. + * 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 (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) + { + /* If does not make sense to fallback to the rfc4880 + * required 3DES if we will reject that algo later. Thus we + * fallback to AES anticipating RFC4880bis rules. */ + if (opt.flags.allow_old_cipher_algos) + dek->algo = CIPHER_ALGO_3DES; + else + dek->algo = CIPHER_ALGO_AES; + } + + /* 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; +} + + +/* 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; + + /* First check whether we should use the algo at all. */ + if (openpgp_cipher_blocklen (dek->algo) < 16 + && !opt.flags.allow_old_cipher_algos) + { + log_error (_("cipher algorithm '%s' may not be used for encryption\n"), + openpgp_cipher_algo_name (dek->algo)); + if (!opt.quiet) + log_info (_("(use option \"%s\" to override)\n"), + "--allow-old-cipher-algos"); + err = gpg_error (GPG_ERR_CIPHER_ALGO); + goto leave; + } + + /* Now check the compliance. */ + 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) + { + compliance_failure (); + err = gpg_error (GPG_ERR_FORBIDDEN); + goto leave; + } + + leave: + return err; +} + + /* Encrypt a session key using DEK and store a pointer to the result * at R_ENCKEY and its length at R_ENCKEYLEN. * @@ -647,7 +784,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. */ @@ -736,127 +872,11 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, } /* 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. - Note that we do not support v3 keys since version 2.2 so the - above description gives only historical background. */ - if (cfx.dek->algo == -1) - { - /* If does not make sense to fallback to the rfc4880 - * required 3DES if we will reject that algo later. Thus we - * fallback to AES anticipating RFC4880bis rules. */ - if (opt.flags.allow_old_cipher_algos) - cfx.dek->algo = CIPHER_ALGO_3DES; - else - cfx.dek->algo = CIPHER_ALGO_AES; - } + cfx.dek = create_dek_with_warnings (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 - identify 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; - } - - if (openpgp_cipher_blocklen (cfx.dek->algo) < 16 - && !opt.flags.allow_old_cipher_algos) - { - log_error (_("cipher algorithm '%s' may not be used for encryption\n"), - openpgp_cipher_algo_name (cfx.dek->algo)); - if (!opt.quiet) - log_info (_("(use option \"%s\" to override)\n"), - "--allow-old-cipher-algos"); - rc = gpg_error (GPG_ERR_CIPHER_ALGO); - goto leave; - } - - /* 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; - } + rc = check_encryption_compliance (cfx.dek, pk_list); + if (rc) + goto leave; cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo); if (!cfx.dek->use_aead) @@ -1040,41 +1060,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 (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 identify 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_aead = use_aead (efx->pk_list, efx->cfx.dek->algo); if (!efx->cfx.dek->use_aead)