From be8ca8852629786266db4d3d69b2c2fb03bd6365 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Tue, 30 May 2017 14:30:24 +0200 Subject: [PATCH] gpg: Report compliance with CO_DE_VS. * common/compliance.c (gnupg_pk_is_compliant): Add DSA with certain parameters. (gnupg_cipher_is_compliant): New function. (gnupg_digest_is_compliant): Likewise. * common/compliance.h (gnupg_cipher_is_compliant): New prototype. (gnupg_digest_is_compliant): Likewise. * common/status.h (STATUS_DECRYPTION_COMPLIANCE_MODE): New status. (STATUS_VERIFICATION_COMPLIANCE_MODE): Likewise. * doc/DETAILS: Document the new status lines. * g10/mainproc.c (proc_encrypted): Compute compliance with CO_DE_VS and report that using the new status line. (check_sig_and_print): Likewise. * sm/decrypt.c (gpgsm_decrypt): Likewise. * sm/verify.c (gpgsm_verify): Likewise. -- When decrypting data and verifying signatures, report whether the operations are in compliance with the criteria for data classified as VS-NfD. This information will be picked up by the frontend and presented to the user. GnuPG-bug-id: 3059 Signed-off-by: Justus Winter --- common/compliance.c | 72 +++++++++++++++++++++++++++++++++++++++++++-- common/compliance.h | 4 +++ common/status.h | 3 ++ doc/DETAILS | 11 +++++++ g10/mainproc.c | 48 ++++++++++++++++++++++++++++++ sm/decrypt.c | 25 ++++++++++++++++ sm/verify.c | 11 +++++++ 7 files changed, 171 insertions(+), 3 deletions(-) diff --git a/common/compliance.c b/common/compliance.c index c0b69843b..80134d6b6 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -45,8 +45,8 @@ int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, gcry_mpi_t key[], unsigned int keylength, const char *curvename) { - enum { is_rsa, is_pgp5, is_elg_sign, is_ecc } algotype; - int result; + enum { is_rsa, is_dsa, is_pgp5, is_elg_sign, is_ecc } algotype; + int result = 0; switch (algo) { @@ -56,8 +56,11 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, algotype = is_rsa; break; - case PUBKEY_ALGO_ELGAMAL_E: case PUBKEY_ALGO_DSA: + algotype = is_dsa; + break; + + case PUBKEY_ALGO_ELGAMAL_E: algotype = is_pgp5; break; @@ -91,6 +94,16 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, || keylength == 4096); break; + case is_dsa: + if (key) + { + size_t L = gcry_mpi_get_nbits (key[0] /* p */); + size_t N = gcry_mpi_get_nbits (key[1] /* q */); + result = (L == 256 + && (N == 2048 || N == 3072)); + } + break; + case is_ecc: if (!curvename && key) { @@ -126,6 +139,59 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, } +/* Return true if CIPHER is compliant to the give COMPLIANCE mode. */ +int +gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, cipher_algo_t cipher) +{ + switch (compliance) + { + case CO_DE_VS: + switch (cipher) + { + case CIPHER_ALGO_AES: + case CIPHER_ALGO_AES192: + case CIPHER_ALGO_AES256: + case CIPHER_ALGO_3DES: + return 1; + default: + return 0; + } + log_assert (!"reached"); + + default: + return 0; + } + + log_assert (!"reached"); +} + + +/* Return true if DIGEST is compliant to the give COMPLIANCE mode. */ +int +gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, 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; + default: + return 0; + } + log_assert (!"reached"); + + default: + return 0; + } + + 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 123bd1b50..4f78ad42f 100644 --- a/common/compliance.h +++ b/common/compliance.h @@ -42,6 +42,10 @@ enum gnupg_compliance_mode int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, 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); +int gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, + digest_algo_t digest); const char *gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance); #endif /*GNUPG_COMMON_COMPLIANCE_H*/ diff --git a/common/status.h b/common/status.h index 8831a0f5f..0250a656f 100644 --- a/common/status.h +++ b/common/status.h @@ -141,6 +141,9 @@ enum STATUS_TOFU_STATS_SHORT, STATUS_TOFU_STATS_LONG, + STATUS_DECRYPTION_COMPLIANCE_MODE, + STATUS_VERIFICATION_COMPLIANCE_MODE, + STATUS_TRUNCATED, STATUS_MOUNTPOINT, STATUS_BLOCKDEV, diff --git a/doc/DETAILS b/doc/DETAILS index 1624315ff..01b5cf9c3 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -638,6 +638,17 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: This indicates that a signature subpacket was seen. The format is the same as the "spk" record above. +*** DECRYPTION_COMPLIANCE_MODE + Indicates that the current decryption operation is in compliance + with the given set of modes. "flags" is a space separated list of + numerical flags, see "Field 18 - Compliance flags" above. + +*** VERIFICATION_COMPLIANCE_MODE + Indicates that the current signature verification operation is in + compliance with the given set of modes. "flags" is a space + separated list of numerical flags, see "Field 18 - Compliance + flags" above. + ** Key related *** INV_RECP, INV_SGNR The two similar status codes: diff --git a/g10/mainproc.c b/g10/mainproc.c index 9500081d5..21ea6cafb 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -39,6 +39,7 @@ #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. */ @@ -599,6 +600,44 @@ proc_encrypted (CTX c, PACKET *pkt) else if (!c->dek) result = GPG_ERR_NO_SECKEY; + /* Compute compliance with CO_DE_VS. */ + if (!result && is_status_enabled () + /* Symmetric encryption voids compliance. */ + && c->symkeys == 0 + /* Overriding session key voids compliance. */ + && opt.override_session_key == NULL + /* Check symmetric cipher. */ + && gnupg_cipher_is_compliant (CO_DE_VS, c->dek->algo)) + { + struct kidlist_item *i; + int compliant = 1; + PKT_public_key *pk = xmalloc (sizeof *pk); + + log_assert (c->pkenc_list || !"where else did the session key come from!?"); + + /* 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->kid) != 0 + || ! gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 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 ); @@ -2196,6 +2235,15 @@ check_sig_and_print (CTX c, kbnode_t node) } } + /* Compute compliance with CO_DE_VS. */ + if (pk && is_status_enabled () + && gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 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 ); diff --git a/sm/decrypt.c b/sm/decrypt.c index f8b01994c..aa621ddf3 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -32,6 +32,7 @@ #include "keydb.h" #include "../common/i18n.h" +#include "../common/compliance.h" struct decrypt_filter_parm_s { @@ -325,6 +326,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) 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); @@ -356,6 +358,10 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) goto leave; } + /* For CMS, CO_DE_VS demands CBC mode. */ + is_de_vs = (mode == GCRY_CIPHER_MODE_CBC + && gnupg_cipher_is_compliant (CO_DE_VS, algo)); + audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo); dfparm.algo = algo; dfparm.mode = mode; @@ -460,7 +466,21 @@ 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); + } + 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); @@ -489,6 +509,11 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) ksba_writer_set_filter (writer, decrypt_filter, &dfparm); + + if (is_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); } diff --git a/sm/verify.c b/sm/verify.c index 7bdc68b80..e19c04e38 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -33,6 +33,7 @@ #include "keydb.h" #include "../common/i18n.h" +#include "../common/compliance.h" static char * strtimestamp_r (ksba_isotime_t atime) @@ -631,6 +632,16 @@ 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;