From fe0b37e123ded51cc5f4cb5e3547fdfbce37a43e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 20 Mar 2017 10:09:40 +0100 Subject: [PATCH] gpg: Add new field no 18 to the colon listing. * 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 --- doc/DETAILS | 9 ++++++ g10/keylist.c | 73 +++++++++++++++++++++++++++++------------ g10/main.h | 3 ++ g10/misc.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++- sm/keylist.c | 18 +++++++++-- 5 files changed, 168 insertions(+), 25 deletions(-) diff --git a/doc/DETAILS b/doc/DETAILS index 8c1187287..cfe70e1df 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -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 diff --git a/g10/keylist.c b/g10/keylist.c index 32cf1e845..b8f32be56 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -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); } diff --git a/g10/main.h b/g10/main.h index f58f04162..c9c345418 100644 --- a/g10/main.h +++ b/g10/main.h @@ -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); diff --git a/g10/misc.c b/g10/misc.c index c69f9948b..0ecdb0478 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -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 diff --git a/sm/keylist.c b/sm/keylist.c index d27d4f441..1b1a261fd 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -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 */