gpg: Add new field no 18 to the colon listing.
authorWerner Koch <wk@gnupg.org>
Mon, 20 Mar 2017 09:09:40 +0000 (10:09 +0100)
committerWerner Koch <wk@gnupg.org>
Mon, 20 Mar 2017 09:09:40 +0000 (10:09 +0100)
* g10/misc.c (gnupg_pk_is_compliant): New.
* g10/keylist.c (print_compliance_flags): New.
(list_keyblock_colon): Call it here.
* sm/keylist.c (print_compliance_flags): New.
(list_cert_colon): Call it here.
--

This patch is to convey information about DE_VS compliant keys to the
caller.  The double digit value is used so that parsers do the right
thing and don't just look for a single digit.

Signed-off-by: Werner Koch <wk@gnupg.org>
doc/DETAILS
g10/keylist.c
g10/main.h
g10/misc.c
sm/keylist.c

index 8c11872..cfe70e1 100644 (file)
@@ -218,6 +218,15 @@ described here.
     For pub, sub, sec, and ssb records this field is used for the ECC
     curve name.
 
+*** Field 18 - Compliance flags
+
+    Space separated list of asserted compliance modes for this key.
+
+    Valid values are:
+
+    - 8  :: The key is compliant with RFC4880bis
+    - 23 :: The key is compliant with compliance mode "de-vs".
+
 ** Special fields
 
 *** PKD - Public key data
index 32cf1e8..b8f32be 100644 (file)
@@ -1170,6 +1170,29 @@ print_revokers (estream_t fp, PKT_public_key * pk)
 }
 
 
+/* Print the compliance flags to field 18.  PK is the public key.
+ * KEYLENGTH is the length of the key in bits and CURVENAME is either
+ * NULL or the name of the curve.  The latter two args are here
+ * merely because the caller has already computed them.  */
+static void
+print_compliance_flags (PKT_public_key *pk,
+                        unsigned int keylength, const char *curvename)
+{
+  int any = 0;
+
+  if (pk->version == 5)
+    {
+      es_fputs ("8", es_stdout);
+      any++;
+    }
+  if (gnupg_pk_is_compliant (CO_DE_VS, pk, keylength, curvename))
+    {
+      es_fputs (any? " 23":"23", es_stdout);
+      any++;
+    }
+}
+
+
 /* List a key in colon mode.  If SECRET is true this is a secret key
    record (i.e. requested via --list-secret-key).  If HAS_SECRET a
    secret key is available even if SECRET is not set.  */
@@ -1191,6 +1214,9 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
   const char *hexgrip = NULL;
   char *serialno = NULL;
   int stubkey;
+  unsigned int keylength;
+  char *curve = NULL;
+  const char *curvename = NULL;
 
   /* Get the keyid from the keyblock.  */
   node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
@@ -1239,14 +1265,16 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
   else
     ownertrust_print = 0;
 
+  keylength = nbits_from_pk (pk);
+
   es_fputs (secret? "sec:":"pub:", es_stdout);
   if (trustletter_print)
     es_putc (trustletter_print, es_stdout);
   es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::",
-          nbits_from_pk (pk),
-          pk->pubkey_algo,
-          (ulong) keyid[0], (ulong) keyid[1],
-          colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
+              keylength,
+              pk->pubkey_algo,
+              (ulong) keyid[0], (ulong) keyid[1],
+              colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
 
   if (ownertrust_print)
     es_putc (ownertrust_print, es_stdout);
@@ -1272,14 +1300,14 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
       || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
       || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
     {
-      char *curve = openpgp_oid_to_str (pk->pkey[0]);
-      const char *name = openpgp_oid_to_curve (curve, 0);
-      if (!name)
-        name = curve;
-      es_fputs (name, es_stdout);
-      xfree (curve);
+      curve = openpgp_oid_to_str (pk->pkey[0]);
+      curvename = openpgp_oid_to_curve (curve, 0);
+      if (!curvename)
+        curvename = curve;
+      es_fputs (curvename, es_stdout);
     }
   es_putc (':', es_stdout);            /* End of field 17. */
+  print_compliance_flags (pk, keylength, curvename);
   es_putc (':', es_stdout);            /* End of field 18. */
   es_putc ('\n', es_stdout);
 
@@ -1380,13 +1408,13 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
              if (trustletter)
                es_fprintf (es_stdout, "%c", trustletter);
            }
+          keylength = nbits_from_pk (pk2);
          es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::",
-                 nbits_from_pk (pk2),
-                 pk2->pubkey_algo,
-                 (ulong) keyid2[0], (ulong) keyid2[1],
-                 colon_datestr_from_pk (pk2), colon_strtime (pk2->expiredate)
-                 /* fixme: add LID and ownertrust here */
-           );
+                      keylength,
+                      pk2->pubkey_algo,
+                      (ulong) keyid2[0], (ulong) keyid2[1],
+                      colon_datestr_from_pk (pk2),
+                      colon_strtime (pk2->expiredate));
          print_capabilities (pk2, NULL);
           es_putc (':', es_stdout);    /* End of field 13. */
           es_putc (':', es_stdout);    /* End of field 14. */
@@ -1405,14 +1433,16 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
               || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA
               || pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
             {
-              char *curve = openpgp_oid_to_str (pk2->pkey[0]);
-              const char *name = openpgp_oid_to_curve (curve, 0);
-              if (!name)
-                name = curve;
-              es_fputs (name, es_stdout);
               xfree (curve);
+              curve = openpgp_oid_to_str (pk2->pkey[0]);
+              curvename = openpgp_oid_to_curve (curve, 0);
+              if (!curvename)
+                curvename = curve;
+              es_fputs (curvename, es_stdout);
             }
           es_putc (':', es_stdout);    /* End of field 17. */
+          print_compliance_flags (pk2, keylength, curvename);
+          es_putc (':', es_stdout);    /* End of field 18. */
          es_putc ('\n', es_stdout);
           print_fingerprint (NULL, pk2, 0);
           if (hexgrip)
@@ -1540,6 +1570,7 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
        }
     }
 
+  xfree (curve);
   xfree (hexgrip_buffer);
   xfree (serialno);
 }
index f58f041..c9c3454 100644 (file)
@@ -125,6 +125,9 @@ int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use);
 int openpgp_pk_algo_usage ( int algo );
 const char *openpgp_pk_algo_name (pubkey_algo_t algo);
 
+int gnupg_pk_is_compliant (int compliance, PKT_public_key *pk,
+                           unsigned int keylength, const char *curvename);
+
 enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo);
 int openpgp_md_test_algo (digest_algo_t algo);
 const char *openpgp_md_algo_name (int algo);
index c69f994..0ecdb04 100644 (file)
@@ -640,7 +640,7 @@ openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use)
   if (!ga)
     return gpg_error (GPG_ERR_PUBKEY_ALGO);
 
-  /* No check whether Libgcrypt has support for the algorithm.  */
+  /* Now check whether Libgcrypt has support for the algorithm.  */
   return gcry_pk_algo_info (ga, GCRYCTL_TEST_ALGO, NULL, &use_buf);
 }
 
@@ -704,6 +704,94 @@ openpgp_pk_algo_name (pubkey_algo_t algo)
 }
 
 
+/* Return true if PK is compliant to the give COMPLIANCE mode.  If
+ * KEYLENGTH and CURVENAME are not 0/NULL the are assumed to be the
+ * already computed values from PK.  */
+int
+gnupg_pk_is_compliant (int compliance, PKT_public_key *pk,
+                       unsigned int keylength, const char *curvename)
+{
+  enum { is_rsa, is_pgp5, is_elg_sign, is_ecc } algotype;
+  int result;
+
+  switch (pk->pubkey_algo)
+    {
+    case PUBKEY_ALGO_RSA:
+    case PUBKEY_ALGO_RSA_E:
+    case PUBKEY_ALGO_RSA_S:
+      algotype = is_rsa;
+      break;
+
+    case PUBKEY_ALGO_ELGAMAL_E:
+    case PUBKEY_ALGO_DSA:
+      algotype = is_pgp5;
+      break;
+
+    case PUBKEY_ALGO_ECDH:
+    case PUBKEY_ALGO_ECDSA:
+    case PUBKEY_ALGO_EDDSA:
+      algotype = is_ecc;
+      break;
+
+    case PUBKEY_ALGO_ELGAMAL:
+      algotype = is_elg_sign;
+      break;
+
+    default: /* Unknown.  */
+      return 0;
+    }
+
+  if (compliance == CO_DE_VS)
+    {
+      char *curve = NULL;
+
+      switch (algotype)
+        {
+        case is_pgp5:
+          result = 0;
+          break;
+
+        case is_rsa:
+          if (!keylength)
+            keylength = nbits_from_pk (pk);
+          result = (keylength >= 2048);
+          break;
+
+        case is_ecc:
+          if (!curvename)
+            {
+              curve = openpgp_oid_to_str (pk->pkey[0]);
+              curvename = openpgp_oid_to_curve (curve, 0);
+              if (!curvename)
+                curvename = curve;
+            }
+
+          result = (curvename
+                    && pk->pubkey_algo != PUBKEY_ALGO_EDDSA
+                    && (!strcmp (curvename, "brainpoolP256r1")
+                        || !strcmp (curvename, "brainpoolP384r1")
+                        || !strcmp (curvename, "brainpoolP512r1")));
+          break;
+
+        default:
+          result = 0;
+        }
+      xfree (curve);
+    }
+  else if (algotype == is_elg_sign)
+    {
+      /* An Elgamal signing key is only RFC-2440 compliant.  */
+      result = (compliance == RFC2440);
+    }
+  else
+    {
+      result = 1; /* Assume compliance.  */
+    }
+
+  return result;
+}
+
+
 /* Explicit mapping of OpenPGP digest algos to Libgcrypt.  */
 /* FIXME: We do not yes use it everywhere.  */
 enum gcry_md_algos
index d27d4f4..1b1a261 100644 (file)
@@ -346,6 +346,14 @@ email_kludge (const char *name)
 }
 
 
+/* Print the compliance flags to field 18.  ALGO is the gcrypt algo
+ * number.  NBITS is the length of the key in bits.  */
+static void
+print_compliance_flags (int algo, unsigned int nbits, estream_t fp)
+{
+  if (algo == GCRY_PK_RSA && nbits >= 2048)
+    es_fputs ("23", fp);
+}
 
 
 /* List one certificate in colon mode */
@@ -496,6 +504,8 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
   print_capabilities (cert, fp);
   /* Field 13, not used: */
   es_putc (':', fp);
+  /* Field 14, not used: */
+  es_putc (':', fp);
   if (have_secret || ctrl->with_secret)
     {
       char *cardsn;
@@ -504,18 +514,20 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
       if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn)
           && (cardsn || ctrl->with_secret))
         {
-          /* Field 14, not used: */
-          es_putc (':', fp);
           /* Field 15:  Token serial number or secret key indicator.  */
           if (cardsn)
             es_fputs (cardsn, fp);
           else if (ctrl->with_secret)
             es_putc ('+', fp);
-          es_putc (':', fp);
         }
       xfree (cardsn);
       xfree (p);
     }
+  es_putc (':', fp);  /* End of field 15. */
+  es_putc (':', fp);  /* End of field 16. */
+  es_putc (':', fp);  /* End of field 17. */
+  print_compliance_flags (algo, nbits, fp);
+  es_putc (':', fp);  /* End of field 18. */
   es_putc ('\n', fp);
 
   /* FPR record */