diff options
author | Justus Winter <justus@g10code.com> | 2017-06-06 16:01:40 +0200 |
---|---|---|
committer | Justus Winter <justus@g10code.com> | 2017-06-08 14:22:54 +0200 |
commit | a64a55e10420cf11e00062b590dffe5d0c3e8192 (patch) | |
tree | 28b9c216316dd1a1aa48b397f68f475023dfea03 | |
parent | gpg: Fix computation of compliance with CO_DE_VS. (diff) | |
download | gnupg2-a64a55e10420cf11e00062b590dffe5d0c3e8192.tar.xz gnupg2-a64a55e10420cf11e00062b590dffe5d0c3e8192.zip |
common,gpg,sm: Restrict the use of algorithms according to CO_DE_VS.
* common/compliance.c (gnupg_pk_is_allowed): New function.
(gnupg_cipher_is_allowed): Likewise.
(gnupg_digest_is_allowed): Likewise.
* common/compliance.h (enum pk_use_case): New definition.
(gnupg_pk_is_allowed): New prototype.
(gnupg_cipher_is_allowed): Likewise.
(gnupg_digest_is_allowed): Likewise.
* g10/decrypt-data.c (decrypt_data): Restrict use of algorithms using
the new predicates.
* g10/encrypt.c (encrypt_crypt): Likewise.
* g10/gpg.c (main): Likewise.
* g10/pubkey-enc.c (get_session_key): Likewise.
* g10/sig-check.c (check_signature2): Likewise.
* g10/sign.c (do_sign): Likewise.
* sm/decrypt.c (gpgsm_decrypt): Likewise.
* sm/encrypt.c (gpgsm_encrypt): Likewise.
* sm/gpgsm.c (main): Likewise.
* sm/sign.c (gpgsm_sign): Likewise.
* sm/verify.c (gpgsm_verify): Likewise.
--
With this change, policies can effectively restrict what algorithms
are used for different purposes. The algorithm policy for CO_DE_VS is
implemented.
GnuPG-bug-id: 3191
Signed-off-by: Justus Winter <justus@g10code.com>
-rw-r--r-- | common/compliance.c | 188 | ||||
-rw-r--r-- | common/compliance.h | 14 | ||||
-rw-r--r-- | g10/decrypt-data.c | 12 | ||||
-rw-r--r-- | g10/encrypt.c | 30 | ||||
-rw-r--r-- | g10/gpg.c | 38 | ||||
-rw-r--r-- | g10/pubkey-enc.c | 26 | ||||
-rw-r--r-- | g10/sig-check.c | 19 | ||||
-rw-r--r-- | g10/sign.c | 23 | ||||
-rw-r--r-- | sm/decrypt.c | 41 | ||||
-rw-r--r-- | sm/encrypt.c | 28 | ||||
-rw-r--r-- | sm/gpgsm.c | 37 | ||||
-rw-r--r-- | sm/sign.c | 27 | ||||
-rw-r--r-- | sm/verify.c | 42 |
13 files changed, 504 insertions, 21 deletions
diff --git a/common/compliance.c b/common/compliance.c index bcf621a45..4078004a9 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -193,6 +193,105 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, } +/* 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, gcry_mpi_t key[], + unsigned int keylength, const char *curvename) +{ + 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_ENCRYPTION: + return 1; + case PK_USE_DECRYPTION: + case PK_USE_SIGNING: + return (keylength == 2048 + || keylength == 3072 + || keylength == 4096); + case PK_USE_VERIFICATION: + return (keylength == 2048 + || keylength == 3072 + || keylength == 4096 + || keylength < 2048); + default: + log_assert (!"reached"); + } + log_assert (!"reached"); + + case PUBKEY_ALGO_DSA: + if (key) + { + size_t L = gcry_mpi_get_nbits (key[0] /* p */); + size_t N = gcry_mpi_get_nbits (key[1] /* q */); + return ((use == PK_USE_SIGNING + && L == 256 + && (N == 2048 || N == 3072)) + || (use == PK_USE_VERIFICATION + && N < 2048)); + } + else + return 0; + log_assert (!"reached"); + + case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_ELGAMAL_E: + return use == PK_USE_ENCRYPTION; + + case PUBKEY_ALGO_ECDH: + return use == PK_USE_ENCRYPTION; + + case PUBKEY_ALGO_ECDSA: + { + int result = 0; + 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"))) + || use == PK_USE_VERIFICATION); + + xfree (curve); + return result; + } + + case PUBKEY_ALGO_EDDSA: + return 0; + + default: + return 0; + } + log_assert (!"reached"); + + default: + /* The default policy is to allow all algorithms. */ + return 1; + } + + log_assert (!"reached"); +} + + /* Return true if (CIPHER, MODE) is compliant to the given COMPLIANCE mode. */ int gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, @@ -232,6 +331,57 @@ gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, } +/* 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) +{ + 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) @@ -260,6 +410,44 @@ gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, digest_algo_t } +/* 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) +{ + 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: + case GCRY_MD_WHIRLPOOL: + 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"); +} + + const char * gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance) { diff --git a/common/compliance.h b/common/compliance.h index e57495da2..b2c969544 100644 --- a/common/compliance.h +++ b/common/compliance.h @@ -41,14 +41,28 @@ enum gnupg_compliance_mode CO_PGP6, CO_PGP7, CO_PGP8, CO_DE_VS }; +enum pk_use_case + { + PK_USE_ENCRYPTION, PK_USE_DECRYPTION, + PK_USE_SIGNING, PK_USE_VERIFICATION, + }; + int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, 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, 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); const char *gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance); struct gnupg_compliance_option diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index 96e2487ec..702330185 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -29,6 +29,7 @@ #include "options.h" #include "../common/i18n.h" #include "../common/status.h" +#include "../common/compliance.h" static int mdc_decode_filter ( void *opaque, int control, IOBUF a, @@ -97,6 +98,17 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) dek->algo_info_printed = 1; } + /* Check compliance. */ + if (! gnupg_cipher_is_allowed (opt.compliance, 0, dek->algo, GCRY_CIPHER_MODE_CFB)) + { + log_error (_ ("you may not use cipher algorithm '%s'" + " while in %s mode\n"), + openpgp_cipher_algo_name (dek->algo), + gnupg_compliance_option_string (opt.compliance)); + rc = gpg_error (GPG_ERR_CIPHER_ALGO); + goto leave; + } + { char buf[20]; diff --git a/g10/encrypt.c b/g10/encrypt.c index 66f49f4a0..ee2f07851 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -38,6 +38,7 @@ #include "../common/i18n.h" #include "../common/status.h" #include "pkglue.h" +#include "../common/compliance.h" static int encrypt_simple( const char *filename, int mode, int use_seskey ); @@ -612,6 +613,35 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, 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 (_ ("you may not use cipher algorithm '%s'" + " while 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; + } + + { + pk_list_t pkr; + for (pkr = pk_list; pkr; pkr = pkr->next) + { + PKT_public_key *pk = pkr->pk; + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_ENCRYPTION, pk->pubkey_algo, + pk->pkey, nbits_from_pk (pk), NULL)) + { + log_error ("key %s not suitable for encryption while in %s mode\n", + keystr_from_pk (pk), + gnupg_compliance_option_string (opt.compliance)); + rc = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } + } + } + cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a @@ -62,6 +62,7 @@ #include "../common/init.h" #include "../common/mbox-util.h" #include "../common/shareddefs.h" +#include "../common/compliance.h" #if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__) #define MY_O_BINARY O_BINARY @@ -3866,6 +3867,43 @@ main (int argc, char **argv) } } + /* Check our chosen algorithms against the list of allowed + * algorithms in the current compliance mode, and fail hard if it + * is not. This is us being nice to the user informing her early + * that the chosen algorithms are not available. We also check + * and enforce this right before the actual operation. */ + if (opt.def_cipher_algo + && ! gnupg_cipher_is_allowed (opt.compliance, + cmd == aEncr + || cmd == aSignEncr + || cmd == aEncrSym + || cmd == aSym + || cmd == aSignSym + || cmd == aSignEncrSym, + opt.def_cipher_algo, + GCRY_CIPHER_MODE_NONE)) + log_error (_ ("you may not use cipher algorithm '%s'" + " while in %s mode\n"), + openpgp_cipher_algo_name (opt.def_cipher_algo), + gnupg_compliance_option_string (opt.compliance)); + + if (opt.def_digest_algo + && ! gnupg_digest_is_allowed (opt.compliance, + cmd == aSign + || cmd == aSignEncr + || cmd == aSignEncrSym + || cmd == aSignSym + || cmd == aClearsign, + opt.def_digest_algo)) + log_error (_ ("you may not use digest algorithm '%s'" + " while in %s mode\n"), + gcry_md_algo_name (opt.def_digest_algo), + gnupg_compliance_option_string (opt.compliance)); + + /* Fail hard. */ + if (log_get_errorcount (0)) + g10_exit (2); + /* Set the random seed file. */ if( use_random_seed ) { char *p = make_filename (gnupg_homedir (), "random_seed", NULL ); diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index d0eaab775..9a7c0911a 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -35,6 +35,7 @@ #include "pkglue.h" #include "call-agent.h" #include "../common/host2net.h" +#include "../common/compliance.h" static gpg_error_t get_it (ctrl_t ctrl, PKT_pubkey_enc *k, @@ -88,7 +89,20 @@ get_session_key (ctrl_t ctrl, PKT_pubkey_enc * k, DEK * dek) sk = xmalloc_clear (sizeof *sk); sk->pubkey_algo = k->pubkey_algo; /* We want a pubkey with this algo. */ if (!(rc = get_seckey (ctrl, sk, k->keyid))) - rc = get_it (ctrl, k, dek, sk, k->keyid); + { + /* Check compliance. */ + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, + sk->pubkey_algo, + sk->pkey, nbits_from_pk (sk), NULL)) + { + log_info ("key %s not suitable for decryption while in %s mode\n", + keystr_from_pk (sk), gnupg_compliance_option_string (opt.compliance)); + free_public_key (sk); + return gpg_error (GPG_ERR_PUBKEY_ALGO); + } + + rc = get_it (ctrl, k, dek, sk, k->keyid); + } } else if (opt.skip_hidden_recipients) rc = gpg_error (GPG_ERR_NO_SECKEY); @@ -116,6 +130,16 @@ get_session_key (ctrl_t ctrl, PKT_pubkey_enc * k, DEK * dek) log_info (_("anonymous recipient; trying secret key %s ...\n"), keystr (keyid)); + /* Check compliance. */ + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, + sk->pubkey_algo, + sk->pkey, nbits_from_pk (sk), NULL)) + { + log_info ("key %s not suitable for decryption while in %s mode\n", + keystr_from_pk (sk), gnupg_compliance_option_string (opt.compliance)); + continue; + } + rc = get_it (ctrl, k, dek, sk, keyid); if (!rc) { diff --git a/g10/sig-check.c b/g10/sig-check.c index 19906e29d..ef97e17d4 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -33,6 +33,7 @@ #include "../common/i18n.h" #include "options.h" #include "pkglue.h" +#include "../common/compliance.h" static int check_signature_end (PKT_public_key *pk, PKT_signature *sig, gcry_md_hd_t digest, @@ -132,6 +133,15 @@ check_signature2 (ctrl_t ctrl, if ( (rc=openpgp_md_test_algo(sig->digest_algo)) ) ; /* We don't have this digest. */ + else if (! gnupg_digest_is_allowed (opt.compliance, 0, sig->digest_algo)) + { + /* Compliance failure. */ + log_info (_ ("you may not use digest algorithm '%s'" + " while in %s mode\n"), + gcry_md_algo_name (sig->digest_algo), + gnupg_compliance_option_string (opt.compliance)); + rc = gpg_error (GPG_ERR_DIGEST_ALGO); + } else if ((rc=openpgp_pk_test_algo(sig->pubkey_algo))) ; /* We don't have this pubkey algo. */ else if (!gcry_md_is_enabled (digest,sig->digest_algo)) @@ -146,6 +156,15 @@ check_signature2 (ctrl_t ctrl, } else if( get_pubkey (ctrl, pk, sig->keyid ) ) rc = gpg_error (GPG_ERR_NO_PUBKEY); + else if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, + pk->pubkey_algo, pk->pkey, nbits_from_pk (pk), + NULL)) + { + /* Compliance failure. */ + log_info ("key %s not suitable for signature verification while in %s mode\n", + keystr_from_pk (pk), gnupg_compliance_option_string (opt.compliance)); + rc = gpg_error (GPG_ERR_PUBKEY_ALGO); + } else if(!pk->flags.valid) { /* You cannot have a good sig from an invalid key. */ diff --git a/g10/sign.c b/g10/sign.c index 6b55fffa0..024dd06e2 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -41,6 +41,7 @@ #include "../common/sysutils.h" #include "call-agent.h" #include "../common/mbox-util.h" +#include "../common/compliance.h" #ifdef HAVE_DOSISH_SYSTEM #define LF "\r\n" @@ -277,6 +278,27 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, if (!mdalgo) mdalgo = gcry_md_get_algo (md); + /* Check compliance. */ + if (! gnupg_digest_is_allowed (opt.compliance, 1, mdalgo)) + { + log_error (_ ("you may not use digest algorithm '%s'" + " while in %s mode\n"), + gcry_md_algo_name (mdalgo), + gnupg_compliance_option_string (opt.compliance)); + err = gpg_error (GPG_ERR_DIGEST_ALGO); + goto leave; + } + + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pksk->pubkey_algo, + pksk->pkey, nbits_from_pk (pksk), NULL)) + { + log_error ("key %s not suitable for signing while in %s mode\n", + keystr_from_pk (pksk), + gnupg_compliance_option_string (opt.compliance)); + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } + print_digest_algo_note (mdalgo); dp = gcry_md_read (md, mdalgo); sig->digest_algo = mdalgo; @@ -321,6 +343,7 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, } xfree (hexgrip); + leave: if (err) log_error (_("signing failed: %s\n"), gpg_strerror (err)); else diff --git a/sm/decrypt.c b/sm/decrypt.c index a36f69027..6909b157f 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -358,6 +358,17 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) goto leave; } + /* Check compliance. */ + if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode)) + { + log_error (_ ("you may not use cipher algorithm '%s'" + " while 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); @@ -465,15 +476,27 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) hexkeygrip = gpgsm_get_keygrip_hexstring (cert); desc = gpgsm_format_keydesc (cert); - /* Check that all certs are compliant with CO_DE_VS. */ - if (is_de_vs) - { - unsigned int nbits; - int pk_algo = gpgsm_get_key_algo_info (cert, &nbits); - - is_de_vs = gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL, - nbits, NULL); - } + { + unsigned int nbits; + int pk_algo = gpgsm_get_key_algo_info (cert, &nbits); + + /* Check compliance. */ + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, + pk_algo, NULL, nbits, NULL)) + { + log_error ("certificate ID 0x%08lX not suitable for " + "decryption while in %s mode\n", + gpgsm_get_short_fingerprint (cert, NULL), + 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, NULL, + nbits, NULL)); + } oops: if (rc) diff --git a/sm/encrypt.c b/sm/encrypt.c index c43a9e6a4..45860a89f 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -33,6 +33,7 @@ #include "keydb.h" #include "../common/i18n.h" +#include "../common/compliance.h" struct dek_s { @@ -405,6 +406,19 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) 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 (_ ("you may not use cipher algorithm '%s'" + " while in %s mode\n"), + opt.def_cipher_algoid, + gnupg_compliance_option_string (opt.compliance)); + rc = gpg_error (GPG_ERR_CIPHER_ALGO); + goto leave; + } + /* Create a session key */ dek = xtrycalloc_secure (1, sizeof *dek); if (!dek) @@ -448,6 +462,20 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) { unsigned char *encval; + /* Check compliance. */ + unsigned int nbits; + int pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_ENCRYPTION, pk_algo, + NULL, nbits, NULL)) + { + log_error ("certificate ID 0x%08lX not suitable for " + "encryption while in %s mode\n", + gpgsm_get_short_fingerprint (cl->cert, NULL), + gnupg_compliance_option_string (opt.compliance)); + rc = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } + rc = encrypt_dek (dek, cl->cert, &encval); if (rc) { diff --git a/sm/gpgsm.c b/sm/gpgsm.c index f749cfd28..c462544e2 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1614,6 +1614,43 @@ main ( int argc, char **argv) } } + /* Check our chosen algorithms against the list of allowed + * algorithms in the current compliance mode, and fail hard if it is + * not. This is us being nice to the user informing her early that + * the chosen algorithms are not available. We also check and + * enforce this right before the actual operation. */ + if (! gnupg_cipher_is_allowed (opt.compliance, + cmd == aEncr || cmd == aSignEncr, + gcry_cipher_map_name (opt.def_cipher_algoid), + GCRY_CIPHER_MODE_NONE) + && ! gnupg_cipher_is_allowed (opt.compliance, + cmd == aEncr || cmd == aSignEncr, + gcry_cipher_mode_from_oid (opt.def_cipher_algoid), + GCRY_CIPHER_MODE_NONE)) + log_error (_ ("you may not use cipher algorithm '%s'" + " while in %s mode\n"), + opt.def_cipher_algoid, gnupg_compliance_option_string (opt.compliance)); + + if (forced_digest_algo + && ! gnupg_digest_is_allowed (opt.compliance, + cmd == aSign + || cmd == aSignEncr + || cmd == aClearsign, + opt.forced_digest_algo)) + log_error (_ ("you may not use digest algorithm '%s'" + " while in %s mode\n"), + forced_digest_algo, gnupg_compliance_option_string (opt.compliance)); + + if (extra_digest_algo + && ! gnupg_digest_is_allowed (opt.compliance, + cmd == aSign + || cmd == aSignEncr + || cmd == aClearsign, + opt.extra_digest_algo)) + log_error (_ ("you may not use digest algorithm '%s'" + " while in %s mode\n"), + forced_digest_algo, gnupg_compliance_option_string (opt.compliance)); + if (log_get_errorcount(0)) gpgsm_exit(2); @@ -460,6 +460,33 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, break; } cl->hash_algo_oid = oid; + + /* Check compliance. */ + if (! gnupg_digest_is_allowed (opt.compliance, 1, cl->hash_algo)) + { + log_error (_ ("you may not use digest algorithm '%s'" + " while in %s mode\n"), + gcry_md_algo_name (cl->hash_algo), + gnupg_compliance_option_string (opt.compliance)); + err = gpg_error (GPG_ERR_DIGEST_ALGO); + goto leave; + } + + { + unsigned int nbits; + int pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); + + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, + NULL, nbits, NULL)) + { + log_error ("certificate ID 0x%08lX not suitable for " + "signing while in %s mode\n", + gpgsm_get_short_fingerprint (cl->cert, NULL), + gnupg_compliance_option_string (opt.compliance)); + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } + } } if (opt.verbose) diff --git a/sm/verify.c b/sm/verify.c index e19c04e38..89f06efee 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -450,6 +450,37 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) goto next_signer; } + /* Check compliance. */ + { + unsigned int nbits; + int pk_algo = gpgsm_get_key_algo_info (cert, &nbits); + + if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, + pk_algo, NULL, nbits, NULL)) + { + log_error ("certificate ID 0x%08lX not suitable for " + "verification while in %s mode\n", + gpgsm_get_short_fingerprint (cert, NULL), + gnupg_compliance_option_string (opt.compliance)); + goto next_signer; + } + + if (! gnupg_digest_is_allowed (opt.compliance, 0, sigval_hash_algo)) + { + log_error (_ ("you may not use digest algorithm '%s'" + " while 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, pk_algo, NULL, nbits, NULL) + && gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo)) + gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE, + gnupg_status_compliance_flag (CO_DE_VS)); + } + log_info (_("Signature made ")); if (*sigtime) dump_isotime (sigtime); @@ -632,17 +663,6 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) (verifyflags & VALIDATE_FLAG_CHAIN_MODEL)? "0 chain": "0 shell"); - /* Check compliance with CO_DE_VS. */ - { - unsigned int nbits; - int pk_algo = gpgsm_get_key_algo_info (cert, &nbits); - - if (gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL, nbits, NULL) - && gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo)) - gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE, - gnupg_status_compliance_flag (CO_DE_VS)); - } - next_signer: rc = 0; xfree (issuer); |