gpg: Report compliance with CO_DE_VS.
authorJustus Winter <justus@g10code.com>
Tue, 30 May 2017 12:30:24 +0000 (14:30 +0200)
committerJustus Winter <justus@g10code.com>
Thu, 1 Jun 2017 11:16:18 +0000 (13:16 +0200)
* 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 <justus@g10code.com>
common/compliance.c
common/compliance.h
common/status.h
doc/DETAILS
g10/mainproc.c
sm/decrypt.c
sm/verify.c

index c0b6984..80134d6 100644 (file)
@@ -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)
 {
index 123bd1b..4f78ad4 100644 (file)
@@ -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*/
index 8831a0f..0250a65 100644 (file)
@@ -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,
index 1624315..01b5cf9 100644 (file)
@@ -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 <flags>
+    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 <flags>
+    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:
index 9500081..21ea6ca 100644 (file)
@@ -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 );
index f8b0199..aa621dd 100644 (file)
@@ -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);
                 }
index 7bdc68b..e19c04e 100644 (file)
@@ -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;