1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02:00

gpg: Add option --assert-pubkey_algo.

* g10/keyid.c (parse_one_algo_string): New.
(compare_pubkey_string_part): New.
(compare_pubkey_string): New.
* g10/verify.c (check_assert_signer_list): New.
* g10/mainproc.c (check_sig_and_print): Call check_assert_pubkey_algo.
* g10/options.h (opt): Add field assert_pubkey_algos.
* g10/gpg.c (oAssertPubkeyAlgo): New.
(opts): Add "--assert-pubkey_algo".
(assert_pubkey_algo_false): New.
(main): Parse option.
(g10_exit): Reorder RC modifications.  Check assert_pubkey_algo_false.
* common/status.h (ASSERT_PUBKEY_ALGOS): new.
* common/t-support.h (LEAN_T_SUPPORT): Use a simplified version if
this macro is set.

* g10/gpgv.c (oAssertPubkeyAlgo): New.
(opts): Add "--assert-pubkey_algo".
(assert_pubkey_algo_false): New.
(main): Parse option.
(g10_exit): Check assert_pubkey_algo_false.

* g10/t-keyid.c: New.
* g10/Makefile.am: Add t-keyid.
* g10/test-stubs.c: Add assert_pubkey_algos and assert_signer_list and
remove from other tests.
(check_assert_signer_list): Ditto.
(check_assert_pubkey_algo): Ditto.
--

GnuPG-bug-id: 6946
This commit is contained in:
Werner Koch 2024-02-10 14:24:50 +01:00
parent 5842eee805
commit 302afcb6f6
No known key found for this signature in database
GPG key ID: E3FDFF218E45B72B
20 changed files with 424 additions and 58 deletions

View file

@ -183,7 +183,7 @@ gpgv_LDFLAGS =
t_common_ldadd =
module_tests = t-rmd160 t-keydb t-keydb-get-keyblock t-stutter
module_tests = t-rmd160 t-keydb t-keydb-get-keyblock t-stutter t-keyid
t_rmd160_SOURCES = t-rmd160.c rmd160.c
t_rmd160_LDADD = $(t_common_ldadd)
t_keydb_SOURCES = t-keydb.c test-stubs.c $(common_source)
@ -200,6 +200,10 @@ t_stutter_SOURCES = t-stutter.c test-stubs.c \
t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
$(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \
$(LIBICONV) $(t_common_ldadd)
t_keyid_SOURCES = t-keyid.c test-stubs.c $(common_source)
t_keyid_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
$(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \
$(LIBICONV) $(t_common_ldadd)
$(PROGRAMS): $(needed_libs) ../common/libgpgrl.a

View file

@ -451,6 +451,7 @@ enum cmd_and_opt_values
oCompatibilityFlags,
oAddDesigRevoker,
oAssertSigner,
oAssertPubkeyAlgo,
oKbxBufferSize,
oNoop
@ -715,6 +716,7 @@ static gpgrt_opt_t opts[] = {
#endif
ARGPARSE_s_s (oAddDesigRevoker, "add-desig-revoker", "@"),
ARGPARSE_s_s (oAssertSigner, "assert-signer", "@"),
ARGPARSE_s_s (oAssertPubkeyAlgo,"assert-pubkey-algo", "@"),
ARGPARSE_header ("Input", N_("Options controlling the input")),
@ -1044,9 +1046,12 @@ static struct compatibility_flags_s compatibility_flags [] =
/* Can be set to true to force gpg to return with EXIT_FAILURE. */
int g10_errors_seen = 0;
/* If opt.assert_signer_list is used and this variabale is not true
/* If opt.assert_signer_list is used and this variable is not true
* gpg will be forced to return EXIT_FAILURE. */
int assert_signer_true = 0;
/* If opt.assert_pubkey_algo is used and this variable is not true
* gpg will be forced to return EXIT_FAILURE. */
int assert_pubkey_algo_false = 0;
static int utf8_strings =
@ -3770,6 +3775,18 @@ main (int argc, char **argv)
add_to_strlist (&opt.assert_signer_list, pargs.r.ret_str);
break;
case oAssertPubkeyAlgo:
if (!opt.assert_pubkey_algos)
opt.assert_pubkey_algos = xstrdup (pargs.r.ret_str);
else
{
char *tmp = opt.assert_pubkey_algos;
opt.assert_pubkey_algos = xstrconcat (tmp, ",",
pargs.r.ret_str, NULL);
xfree (tmp);
}
break;
case oKbxBufferSize:
keybox_set_buffersize (pargs.r.ret_ulong, 0);
break;
@ -5472,6 +5489,17 @@ emergency_cleanup (void)
void
g10_exit( int rc )
{
if (rc)
;
else if (log_get_errorcount(0))
rc = 2;
else if (g10_errors_seen)
rc = 1;
else if (opt.assert_signer_list && !assert_signer_true)
rc = 1;
else if (opt.assert_pubkey_algos && assert_pubkey_algo_false)
rc = 1;
/* If we had an error but not printed an error message, do it now.
* Note that write_status_failure will never print a second failure
* status line. */
@ -5496,15 +5524,6 @@ g10_exit( int rc )
gnupg_block_all_signals ();
emergency_cleanup ();
if (rc)
;
else if (log_get_errorcount(0))
rc = 2;
else if (g10_errors_seen)
rc = 1;
else if (opt.assert_signer_list && !assert_signer_true)
rc = 1;
exit (rc);
}

View file

@ -68,6 +68,7 @@ enum cmd_and_opt_values {
oWeakDigest,
oEnableSpecialFilenames,
oDebug,
oAssertPubkeyAlgo,
aTest
};
@ -91,6 +92,7 @@ static gpgrt_opt_t opts[] = {
N_("|ALGO|reject signatures made with ALGO")),
ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"),
ARGPARSE_s_s (oDebug, "debug", "@"),
ARGPARSE_s_s (oAssertPubkeyAlgo,"assert-pubkey-algo", "@"),
ARGPARSE_end ()
};
@ -119,6 +121,7 @@ static struct debug_flags_s debug_flags [] =
int g10_errors_seen = 0;
int assert_signer_true = 0;
int assert_pubkey_algo_false = 0;
static char *
make_libversion (const char *libname, const char *(*getfnc)(const char*))
@ -251,6 +254,19 @@ main( int argc, char **argv )
case oEnableSpecialFilenames:
enable_special_filenames ();
break;
case oAssertPubkeyAlgo:
if (!opt.assert_pubkey_algos)
opt.assert_pubkey_algos = xstrdup (pargs.r.ret_str);
else
{
char *tmp = opt.assert_pubkey_algos;
opt.assert_pubkey_algos = xstrconcat (tmp, ",",
pargs.r.ret_str, NULL);
xfree (tmp);
}
break;
default : pargs.err = ARGPARSE_PRINT_ERROR; break;
}
}
@ -288,10 +304,18 @@ main( int argc, char **argv )
void
g10_exit( int rc )
g10_exit (int rc)
{
rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0;
exit(rc );
if (rc)
;
else if (log_get_errorcount(0))
rc = 2;
else if (g10_errors_seen)
rc = 1;
else if (opt.assert_pubkey_algos && assert_pubkey_algo_false)
rc = 1;
exit (rc);
}

View file

@ -487,6 +487,7 @@ const char *key_origin_string (int origin);
/*-- keyid.c --*/
int pubkey_letter( int algo );
char *pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize);
int compare_pubkey_string (const char *astr, const char *bstr);
#define PUBKEY_STRING_SIZE 32
u32 v3_keyid (gcry_mpi_t a, u32 *ki);
void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk );

View file

@ -140,6 +140,130 @@ pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize)
}
/* Helper for compare_pubkey_string. This skips leading spaces,
* commas and optional condition operators and returns a pointer to
* the first non-space character or NULL in case of an error. The
* length of a prefix consisting of letters is then returned ar PFXLEN
* and the value of the number (e.g. 384 for "brainpoolP384r1") at
* NUMBER. R_LENGTH receives the entire length of the algorithm name
* which is terminated by a space, nul, or a comma. If R_CONDITION is
* not NULL, 0 is stored for a leading "=", 1 for a ">", 2 for a ">=",
* -1 for a "<", and -2 for a "<=". If R_CONDITION is NULL no
* condition prefix is allowed. */
static const char *
parse_one_algo_string (const char *str, size_t *pfxlen, unsigned int *number,
size_t *r_length, int *r_condition)
{
int condition = 0;
const char *result;
while (spacep (str) || *str ==',')
str++;
if (!r_condition)
;
else if (*str == '>' && str[1] == '=')
condition = 2, str += 2;
else if (*str == '>' )
condition = 1, str += 1;
else if (*str == '<' && str[1] == '=')
condition = -2, str += 2;
else if (*str == '<')
condition = -1, str += 1;
else if (*str == '=') /* Default. */
str += 1;
if (!alphap (str))
return NULL; /* Error. */
*pfxlen = 1;
for (result = str++; alphap (str); str++)
++*pfxlen;
while (*str == '-' || *str == '+')
str++;
*number = atoi (str);
while (*str && !spacep (str) && *str != ',')
str++;
*r_length = str - result;
if (r_condition)
*r_condition = condition;
return result;
}
/* Helper for compare_pubkey_string. If BPARSED is set to 0 on
* return, an error in ASTR or BSTR was found and further checks are
* not possible. */
static int
compare_pubkey_string_part (const char *astr, const char *bstr_arg,
size_t *bparsed)
{
const char *bstr = bstr_arg;
size_t alen, apfxlen, blen, bpfxlen;
unsigned int anumber, bnumber;
int condition;
*bparsed = 0;
astr = parse_one_algo_string (astr, &apfxlen, &anumber, &alen, &condition);
if (!astr)
return 0; /* Invalid algorithm name. */
bstr = parse_one_algo_string (bstr, &bpfxlen, &bnumber, &blen, &condition);
if (!bstr)
return 0; /* Invalid algorithm name. */
*bparsed = blen + (bstr - bstr_arg);
if (apfxlen != bpfxlen || ascii_strncasecmp (astr, bstr, apfxlen))
return 0; /* false. */
switch (condition)
{
case 2: return anumber >= bnumber;
case 1: return anumber > bnumber;
case -1: return anumber < bnumber;
case -2: return anumber <= bnumber;
}
return alen == blen && !ascii_strncasecmp (astr, bstr, alen);
}
/* Check whether ASTR matches the constraints given by BSTR. ASTR may
* be any algo string like "rsa2048", "ed25519" and BSTR may be a
* constraint which is in the simplest case just another algo string.
* BSTR may have more that one string in which case they are comma
* separated and any match will return true. It is possible to prefix
* BSTR with ">", ">=", "<=", or "<". That prefix operator is applied
* to the number part of the algorithm, i.e. the first sequence of
* digits found before end-of-string or a comma. Examples:
*
* | ASTR | BSTR | result |
* |----------+----------------------+--------|
* | rsa2048 | rsa2048 | true |
* | rsa2048 | >=rsa2048 | true |
* | rsa2048 | >rsa2048 | false |
* | ed25519 | >rsa1024 | false |
* | ed25519 | ed25519 | true |
* | nistp384 | >nistp256 | true |
* | nistp521 | >=rsa3072, >nistp384 | true |
*/
int
compare_pubkey_string (const char *astr, const char *bstr)
{
size_t bparsed;
int result;
while (*bstr)
{
result = compare_pubkey_string_part (astr, bstr, &bparsed);
if (result)
return 1;
if (!bparsed)
return 0; /* Syntax error in ASTR or BSTR. */
bstr += bparsed;
}
return 0;
}
/* Hash a public key and allow to specify the to be used format.
* Note that if the v5 format is requested for a v4 key, a 0x04 as
* version is hashed instead of the 0x05. */

View file

@ -84,6 +84,7 @@ struct weakhash
/*-- gpg.c --*/
extern int g10_errors_seen;
extern int assert_signer_true;
extern int assert_pubkey_algo_false;
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
void g10_exit(int rc) __attribute__ ((__noreturn__));
@ -494,6 +495,7 @@ int verify_signatures (ctrl_t ctrl, int nfiles, char **files );
int verify_files (ctrl_t ctrl, int nfiles, char **files );
int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp);
void check_assert_signer_list (const char *mainpkhex, const char *pkhex);
void check_assert_pubkey_algo (const char *algostr, const char *pkhex);
/*-- decrypt.c --*/
int decrypt_message (ctrl_t ctrl, const char *filename );

View file

@ -1876,6 +1876,8 @@ check_sig_and_print (CTX c, kbnode_t node)
const void *extrahash = NULL;
size_t extrahashlen = 0;
kbnode_t included_keyblock = NULL;
char pkstrbuf[PUBKEY_STRING_SIZE] = { 0 };
if (opt.skip_verify)
{
@ -2409,8 +2411,14 @@ check_sig_and_print (CTX c, kbnode_t node)
show_notation (sig, 0, 2, 0);
}
/* Fill PKSTRBUF with the algostring in case we later need it. */
if (pk)
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf);
/* For good signatures print the VALIDSIG status line. */
if (!rc && (is_status_enabled () || opt.assert_signer_list) && pk)
if (!rc && (is_status_enabled ()
|| opt.assert_signer_list
|| opt.assert_pubkey_algos) && pk)
{
char pkhex[MAX_FINGERPRINT_LEN*2+1];
char mainpkhex[MAX_FINGERPRINT_LEN*2+1];
@ -2432,6 +2440,8 @@ check_sig_and_print (CTX c, kbnode_t node)
mainpkhex);
/* Handle the --assert-signer option. */
check_assert_signer_list (mainpkhex, pkhex);
/* Handle the --assert-pubkey-algo option. */
check_assert_pubkey_algo (pkstrbuf, pkhex);
}
/* Print compliance warning for Good signatures. */
@ -2464,13 +2474,6 @@ check_sig_and_print (CTX c, kbnode_t node)
if (opt.verbose)
{
char pkstrbuf[PUBKEY_STRING_SIZE];
if (pk)
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf);
else
*pkstrbuf = 0;
log_info (_("%s signature, digest algorithm %s%s%s\n"),
sig->sig_class==0x00?_("binary"):
sig->sig_class==0x01?_("textmode"):_("unknown"),

View file

@ -241,6 +241,10 @@ struct
* modify to be uppercase if they represent a fingerrint */
strlist_t assert_signer_list;
/* A single string with the comma delimited args from
* --assert-pubkey_algo. */
char *assert_pubkey_algos;
struct
{
/* If set, require an 0x19 backsig to be present on signatures

View file

@ -67,12 +67,3 @@ do_test (int argc, char *argv[])
release_kbnode (kb1);
xfree (ctrl);
}
int assert_signer_true = 0;
void
check_assert_signer_list (const char *mainpkhex, const char *pkhex)
{
(void)mainpkhex;
(void)pkhex;
}

View file

@ -105,13 +105,3 @@ do_test (int argc, char *argv[])
keydb_release (hd2);
xfree (ctrl);
}
int assert_signer_true = 0;
void
check_assert_signer_list (const char *mainpkhex, const char *pkhex)
{
(void)mainpkhex;
(void)pkhex;
}

129
g10/t-keyid.c Normal file
View file

@ -0,0 +1,129 @@
/* t-keyid.c - Tests for keyid.c.
* Copyright (C) 2024 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEAN_T_SUPPORT 1
#define PGM "t-keyid"
#include "gpg.h"
#include "keydb.h"
#include "../common/t-support.h"
static int verbose;
static void
test_compare_pubkey_string (void)
{
static struct { const char *astr; const char *bstr; int expected; } t[] =
{
{ "rsa2048" , "rsa2048" , 1 },
{ "rsa2048" , ">=rsa2048" , 1 },
{ "rsa2048" , ">rsa2048" , 0 },
{ "ed25519" , ">rsa1024" , 0 },
{ "ed25519" , "ed25519" , 1 },
{ "ed25519" , ",,,=ed25519" , 1 },
{ "nistp384" , ">nistp256" , 1 },
{ "nistp521" , ">=rsa3072, >nistp384", 1 },
{ " nistp521" , ">=rsa3072, >nistp384 ", 1 },
{ " nistp521 " , " >=rsa3072, >nistp384 ", 1 },
{ " =nistp521 " , " >=rsa3072, >nistp384,,", 1 },
{ "nistp384" , ">nistp384" , 0 },
{ "nistp384" , ">=nistp384" , 1 },
{ "brainpoolP384" , ">=brainpoolp256", 1 },
{ "brainpoolP384" , ">brainpoolp384" , 0 },
{ "brainpoolP384" , ">=brainpoolp384", 1 },
{ "brainpoolP256r1", ">brainpoolp256r1", 0 },
{ "brainpoolP384r1", ">brainpoolp384r1" , 0 },
{ "brainpoolP384r1", ">=brainpoolp384r1", 1 },
{ "brainpoolP384r1", ">=brainpoolp384" , 1 },
{ "", "", 0}
};
int idx;
int result;
for (idx=0; idx < DIM(t); idx++)
{
result = compare_pubkey_string (t[idx].astr, t[idx].bstr);
if (result != t[idx].expected)
{
fail (idx);
if (verbose)
log_debug ("\"%s\", \"%s\" want %d got %d\n",
t[idx].astr, t[idx].bstr, t[idx].expected, result);
}
}
}
int
main (int argc, char **argv)
{
int last_argc = -1;
no_exit_on_fail = 1;
if (argc)
{ argc--; argv++; }
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
fputs ("usage: " PGM " [FILE]\n"
"Options:\n"
" --verbose Print timings etc.\n"
" --debug Flyswatter\n"
, stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose += 2;
argc--; argv++;
}
else if (!strncmp (*argv, "--", 2))
{
fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
exit (1);
}
}
test_compare_pubkey_string ();
return !!errcount;
}

View file

@ -611,12 +611,3 @@ do_test (int argc, char *argv[])
xfree (filename);
}
int assert_signer_true = 0;
void
check_assert_signer_list (const char *mainpkhex, const char *pkhex)
{
(void)mainpkhex;
(void)pkhex;
}

View file

@ -43,6 +43,9 @@
#include "call-agent.h"
int g10_errors_seen;
int assert_signer_true = 0;
int assert_pubkey_algo_false = 0;
void
@ -580,3 +583,18 @@ impex_filter_getval (void *cookie, const char *propname)
(void)propname;
return NULL;
}
void
check_assert_signer_list (const char *mainpkhex, const char *pkhex)
{
(void)mainpkhex;
(void)pkhex;
}
void
check_assert_pubkey_algo (const char *algostr, const char *pkhex)
{
(void)algostr;
(void)pkhex;
}

View file

@ -15,6 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>

View file

@ -333,7 +333,7 @@ check_assert_signer_list (const char *mainpkhex, const char *pkhex)
assert_signer_true = 1;
write_status_text (STATUS_ASSERT_SIGNER, item->d);
if (!opt.quiet)
log_info ("signer '%s' matched\n", item->d);
log_info ("asserted signer '%s'\n", item->d);
goto leave;
}
}
@ -388,7 +388,7 @@ check_assert_signer_list (const char *mainpkhex, const char *pkhex)
assert_signer_true = 1;
write_status_text (STATUS_ASSERT_SIGNER, p);
if (!opt.quiet)
log_info ("signer '%s' matched '%s', line %d\n",
log_info ("asserted signer '%s' (%s:%d)\n",
p, fname, lnr);
goto leave;
}
@ -405,3 +405,32 @@ check_assert_signer_list (const char *mainpkhex, const char *pkhex)
leave:
es_fclose (fp);
}
/* This function shall be called with the signer's public key
* algorithm ALGOSTR iff a signature is fully valid. If the option
* --assert-pubkey-algo is active the functions checks whether the
* signing key's algo is valid according to that list; in this case a
* global flag is set. */
void
check_assert_pubkey_algo (const char *algostr, const char *pkhex)
{
if (!opt.assert_pubkey_algos)
return; /* Nothing to do. */
if (compare_pubkey_string (algostr, opt.assert_pubkey_algos))
{
write_status_strings (STATUS_ASSERT_PUBKEY_ALGO,
pkhex, " 1 ", algostr, NULL);
if (!opt.quiet)
log_info ("asserted signer '%s' with algo %s\n", pkhex, algostr);
}
else
{
if (!opt.quiet)
log_info ("denied signer '%s' with algo %s\n", pkhex, algostr);
assert_pubkey_algo_false = 1;
write_status_strings (STATUS_ASSERT_PUBKEY_ALGO,
pkhex, " 0 ", algostr, NULL);
}
}