Allow verification of some broken S-TRUST generated signatures.

This commit is contained in:
Werner Koch 2007-12-13 15:45:40 +00:00
parent aeb5a65f7c
commit 9d66580cff
19 changed files with 276 additions and 25 deletions

3
NEWS
View File

@ -23,6 +23,9 @@ Noteworthy changes in version 2.0.8
* Creating DSA2 keys is now possible.
* New option --extra-digest-algo for gpgsm to allow verification of
broken signatures.
Noteworthy changes in version 2.0.7 (2007-09-10)
------------------------------------------------

View File

@ -1,3 +1,9 @@
2007-12-13 Werner Koch <wk@g10code.com>
* sexputil.c (hash_algo_from_sigval): New.
* t-sexputil.c: New.
* Makefile.am (module_tests): Add it.
2007-12-11 Werner Koch <wk@g10code.com>
* asshelp.c (send_pinentry_environment): Allow using of old
@ -1126,7 +1132,8 @@
(atoi_1,atoi_2,atoi_4,xtoi_1,xtoi_2): New.
Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Copyright 2001, 2002, 2003, 2004, 2005, 2006,
2007 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without

View File

@ -107,7 +107,7 @@ status-codes.h: Makefile mkstrtable.awk exstatus.awk status.h
#
# Module tests
#
module_tests = t-convert t-gettime t-sysutils
module_tests = t-convert t-gettime t-sysutils t-sexputil
module_maint_tests = t-helpfile
t_common_ldadd = libcommon.a ../jnlib/libjnlib.a ../gl/libgnu.a \
@ -117,5 +117,5 @@ t_convert_LDADD = $(t_common_ldadd)
t_gettime_LDADD = $(t_common_ldadd)
t_sysutils_LDADD = $(t_common_ldadd)
t_helpfile_LDADD = $(t_common_ldadd)
t_sexputil_LDADD = $(t_common_ldadd)

View File

@ -44,7 +44,7 @@ snext (unsigned char const **buf)
lists and may be passed as a positive number to skip over the
remainder of an S-Expression if the current position is somewhere
in an S-Expression. The function may return an error code if it
encounters an impossible conditions */
encounters an impossible condition. */
static inline gpg_error_t
sskip (unsigned char const **buf, int *depth)
{

View File

@ -1,5 +1,5 @@
/* sexputil.c - Utility functions for S-expressions.
* Copyright (C) 2005 Free Software Foundation, Inc.
* Copyright (C) 2005, 2007 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -40,8 +40,8 @@
KEY is expected to be an canonical encoded S-expression with a
public or private key. KEYLEN is the length of that buffer.
GRIP must be at least 20 bytes long On success 0 is return, on
error an aerror code. */
GRIP must be at least 20 bytes long. On success 0 is returned, on
error an error code. */
gpg_error_t
keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
unsigned char *grip)
@ -143,3 +143,49 @@ make_simple_sexp_from_hexstr (const char *line, size_t *nscanned)
return buf;
}
/* Return the hash algorithm from a KSBA sig-val. SIGVAL is a
canonical encoded S-expression. Return 0 if the hash algorithm is
not encoded in SIG-VAL or it is not supported by libgcrypt. */
int
hash_algo_from_sigval (const unsigned char *sigval)
{
const unsigned char *s = sigval;
size_t n;
int depth;
char buffer[50];
if (!s || *s != '(')
return 0; /* Invalid S-expression. */
s++;
n = snext (&s);
if (!n)
return 0; /* Invalid S-expression. */
if (!smatch (&s, n, "sig-val"))
return 0; /* Not a sig-val. */
if (*s != '(')
return 0; /* Invalid S-expression. */
s++;
/* Skip over the algo+parameter list. */
depth = 1;
if (sskip (&s, &depth) || depth)
return 0; /* Invalid S-expression. */
if (*s != '(')
return 0; /* No futher list. */
/* Check whether this is (hash ALGO). */
s++;
n = snext (&s);
if (!n)
return 0; /* Invalid S-expression. */
if (!smatch (&s, n, "hash"))
return 0; /* Not a "hash" keyword. */
n = snext (&s);
if (!n || n+1 >= sizeof (buffer))
return 0; /* Algorithm string is missing or too long. */
memcpy (buffer, s, n);
buffer[n] = 0;
return gcry_md_map_name (buffer);
}

82
common/t-sexputil.c Normal file
View File

@ -0,0 +1,82 @@
/* t-sexputil.c - Module test for sexputil.c
* Copyright (C) 2007 Free Software Foundation, Inc.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include "util.h"
#define pass() do { ; } while(0)
#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\
__FILE__,__LINE__, (a)); \
exit (1); \
} while(0)
static void
test_hash_algo_from_sigval (void)
{
int algo;
/* A real world example. */
unsigned char example1_rsa_sha1[] =
("\x28\x37\x3A\x73\x69\x67\x2D\x76\x61\x6C\x28\x33\x3A\x72\x73\x61"
"\x28\x31\x3A\x73\x31\x32\x38\x3A\x17\xD2\xE9\x5F\xB4\x24\xD4\x1E"
"\x8C\xEE\x94\xDA\x41\x42\x1F\x26\x5E\xF4\x6D\xEC\x5B\xBD\x5B\x89"
"\x7A\x69\x11\x43\xE9\xD2\x23\x21\x25\x64\xA6\xB0\x56\xEF\xB4\xE9"
"\x06\xB2\x44\xF6\x80\x1E\xFF\x41\x23\xEB\xC9\xFA\xFD\x09\xBF\x9C"
"\x8E\xCF\x7F\xC3\x7F\x3A\x40\x48\x89\xDC\xBA\xB7\xDB\x9E\xF1\xBA"
"\x7C\x08\xEA\x74\x1D\x49\xE7\x65\xEF\x67\x79\xBC\x23\xD9\x49\xCD"
"\x05\x99\xD3\xD8\xB7\x7B\xC7\x0E\xF2\xB3\x01\x48\x0F\xC8\xEB\x05"
"\x7B\xFB\x61\xCC\x41\x04\x74\x6D\x33\x84\xB1\xE6\x6A\xD8\x0F\xBC"
"\x27\xAC\x43\x45\xFA\x04\xD1\x22\x29\x29\x28\x34\x3A\x68\x61\x73"
"\x68\x34\x3A\x73\x68\x61\x31\x29\x29");
/* The same but without the hash algo. */
unsigned char example1_rsa[] =
("\x28\x37\x3A\x73\x69\x67\x2D\x76\x61\x6C\x28\x33\x3A\x72\x73\x61"
"\x28\x31\x3A\x73\x31\x32\x38\x3A\x17\xD2\xE9\x5F\xB4\x24\xD4\x1E"
"\x8C\xEE\x94\xDA\x41\x42\x1F\x26\x5E\xF4\x6D\xEC\x5B\xBD\x5B\x89"
"\x7A\x69\x11\x43\xE9\xD2\x23\x21\x25\x64\xA6\xB0\x56\xEF\xB4\xE9"
"\x06\xB2\x44\xF6\x80\x1E\xFF\x41\x23\xEB\xC9\xFA\xFD\x09\xBF\x9C"
"\x8E\xCF\x7F\xC3\x7F\x3A\x40\x48\x89\xDC\xBA\xB7\xDB\x9E\xF1\xBA"
"\x7C\x08\xEA\x74\x1D\x49\xE7\x65\xEF\x67\x79\xBC\x23\xD9\x49\xCD"
"\x05\x99\xD3\xD8\xB7\x7B\xC7\x0E\xF2\xB3\x01\x48\x0F\xC8\xEB\x05"
"\x7B\xFB\x61\xCC\x41\x04\x74\x6D\x33\x84\xB1\xE6\x6A\xD8\x0F\xBC"
"\x27\xAC\x43\x45\xFA\x04\xD1\x22\x29\x29\x29");
algo = hash_algo_from_sigval (example1_rsa_sha1);
if (algo != GCRY_MD_SHA1)
fail (0);
algo = hash_algo_from_sigval (example1_rsa);
if (algo)
fail (0);
}
int
main (int argc, char **argv)
{
test_hash_algo_from_sigval ();
return 0;
}

View File

@ -164,6 +164,7 @@ gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
unsigned char *make_simple_sexp_from_hexstr (const char *line,
size_t *nscanned);
int hash_algo_from_sigval (const unsigned char *sigval);
/*-- convert.c --*/
int hex2bin (const char *string, void *buffer, size_t length);

View File

@ -1,3 +1,14 @@
2007-12-13 Werner Koch <wk@g10code.com>
* qualified.txt: Add 2 root certs from S-Trust for 2008-2012.
* examples/trustlist.txt: Ditto.
* gpgsm.texi (Esoteric Options): Document --extra-digest-algo.
2007-12-12 Werner Koch <wk@g10code.com>
* gpg.texi: Typo fixes. From Christer Andersson.
2007-12-04 Werner Koch <wk@g10code.com>
* help.txt: New online help file.

View File

@ -182,7 +182,12 @@ such a certificate. You may use the @code{relax} flag in
fingerprint and this flag may only be added manually to
@file{trustlist.txt}.
@item Error message: ``digest algorithm N has not been enabled''
The signature is broken. You may try the option
@option{--extra-digest-algo SHA256} to workaround the problem. The
number N is the internal algorighm indentifier; for example 8 refers to
SHA-256.
@end itemize

View File

@ -38,6 +38,17 @@ DB:45:3D:1B:B0:1A:F3:23:10:6B:DE:D0:09:61:57:AA:F4:25:E0:5B S
# Issuer: /CN=11R-CA 1:PN/O=Bundesnetzagentur/C=DE
A0:8B:DF:3B:AA:EE:3F:9D:64:6C:47:81:23:21:D4:A6:18:81:67:1D S
# S/N: 00B3963E0E6C2D65125853E970665402E5
# Issuer: /CN=S-TRUST Qualified Root CA 2008-001:PN
# /O=Deutscher Sparkassen Verlag GmbH/L=Stuttgart/C=DE
C9:2F:E6:50:DB:32:59:E0:CE:65:55:F3:8C:76:E0:B8:A8:FE:A3:CA S
# S/N: 00C4216083F35C54F67B09A80C3C55FE7D
# Issuer: /CN=S-TRUST Qualified Root CA 2008-002:PN
# /O=Deutscher Sparkassen Verlag GmbH/L=Stuttgart/C=DE
D5:C7:50:F2:FE:4E:EE:D7:C7:B1:E4:13:7B:FB:54:84:3A:7D:97:9B S
#Serial number: 00
# Issuer: /CN=CA Cert Signing Authority/OU=http:\x2f\x2fwww.
# cacert.org/O=Root CA/EMail=support@cacert.org

View File

@ -113,7 +113,7 @@ Developer information:
@node GPG Commands
@section Commands
Commands are not distinguished from options execpt for the fact that
Commands are not distinguished from options except for the fact that
only one command is allowed.
@command{@gpgname} may be run with no commands, in which case it will
@ -876,7 +876,7 @@ encountered, you can explicitly stop parsing by using the special option
@node GPG Configuration Options
@subsection How to change the configuration
These options are used to change the configuraton and are usually found
These options are used to change the configuration and are usually found
in the option file.
@table @gnupgtabopt
@ -2456,13 +2456,13 @@ listed. @option{--list-config} is only usable with
@item --gpgconf-list
@opindex gpgconf-list
This command is simliar to @option{--list-config} but in general only
This command is similar to @option{--list-config} but in general only
internally used by the @command{gpgconf} tool.
@item --gpgconf-test
@opindex gpgconf-test
This is more or less dummy action. However it parses the configuration
file and returns with failure if the configuraion file would prevent
file and returns with failure if the configuration file would prevent
@command{gpg} from startup. Thus it may be used to run a syntax check
on the configuration file.
@ -2560,7 +2560,7 @@ For existing users the a small
helper script is provided to create these files (@pxref{addgnupghome}).
@end ifclear
For internal purposes @command{@gpgname} creates and maintaines a few other
For internal purposes @command{@gpgname} creates and maintains a few other
files; They all live in in the current home directory (@pxref{option
--homedir}). Only the @command{@gpgname} may modify these files.
@ -2686,7 +2686,7 @@ user for the filename.
@include specify-user-id.texi
@end ifset
@mansect return vaue
@mansect return value
@chapheading RETURN VALUE
The program returns 0 if everything was fine, 1 if at least

View File

@ -569,6 +569,19 @@ encryption. For convenience the strings @code{3DES}, @code{AES} and
@table @gnupgtabopt
@item --extra-digest-algo @var{name}
@opindex extra-digest-algo
Sometimes signatures are broken in that they announce a different digest
algorithm than actually used. @command{gpgsm} uses a one-pass data
processing model and thus needs to rely on the announcde digest
algorithms to properly hash the data. As a workaround this option may
be used to tell gpg to also hash the data using the algorithm
@var{name}; this slows processing down a little bit but allows to verify
such broken signatures. If @command{gpgsm} prints an error like
``digest algo 8 has not been enabled'' you may want to try this option,
with @samp{SHA256} for @var{name}.
@item --faked-system-time @var{epoch}
@opindex faked-system-time
This option is only useful for testing; it sets the system time back or

View File

@ -180,6 +180,35 @@ E0:BF:1B:91:91:6B:88:E4:F1:15:92:22:CE:37:23:96:B1:4A:2E:5C de
7A:3C:1B:60:2E:BD:A4:A1:E0:EB:AD:7A:BA:4F:D1:43:69:A9:39:FC de
# ID: 0xA8FEA3CA
# S/N: 00B3963E0E6C2D65125853E970665402E5
# Issuer: /CN=S-TRUST Qualified Root CA 2008-001:PN
# /O=Deutscher Sparkassen Verlag GmbH/L=Stuttgart/C=DE
# Subject: /CN=S-TRUST Qualified Root CA 2008-001:PN
# /O=Deutscher Sparkassen Verlag GmbH/L=Stuttgart/C=DE
# validity: 2008-01-01 00:00:00 through 2012-12-30 23:59:59
# key type: 2048 bit RSA
# key usage: certSign crlSign
# chain length: 1
#[checked: 2007-12-13 via received ZIP file with qualified signature from
# /CN=Dr. Matthias Stehle/O=Deutscher Sparkassenverlag
# /C=DE/SerialNumber=DSV0000000008/SN=Stehle/GN=Matthias Georg]
C9:2F:E6:50:DB:32:59:E0:CE:65:55:F3:8C:76:E0:B8:A8:FE:A3:CA
# ID: 0x3A7D979B
# S/N: 00C4216083F35C54F67B09A80C3C55FE7D
# Issuer: /CN=S-TRUST Qualified Root CA 2008-002:PN
# /O=Deutscher Sparkassen Verlag GmbH/L=Stuttgart/C=DE
# Subject: /CN=S-TRUST Qualified Root CA 2008-002:PN
# /O=Deutscher Sparkassen Verlag GmbH/L=Stuttgart/C=DE
# validity: 2008-01-01 00:00:00 through 2012-12-30 23:59:59
# key type: 2048 bit RSA
# key usage: certSign crlSign
# chain length: 1
#[checked: 2007-12-13 via received ZIP file with qualified signature from
# /CN=Dr. Matthias Stehle/O=Deutscher Sparkassenverlag
# /C=DE/SerialNumber=DSV0000000008/SN=Stehle/GN=Matthias Georg"]
D5:C7:50:F2:FE:4E:EE:D7:C7:B1:E4:13:7B:FB:54:84:3A:7D:97:9B
#*******************************************

View File

@ -121,7 +121,7 @@ This should return the Root cert of the issuer. See note above.
@item By exact match on serial number and issuer's DN.
This is indicated by a hash mark, followed by the hexadecmal
This is indicated by a hash mark, followed by the hexadecimal
representation of the serial number, then followed by a slash and the
RFC-2253 encoded DN of the issuer. See note above.

View File

@ -1,3 +1,10 @@
2007-12-13 Werner Koch <wk@g10code.com>
* gpgsm.c (main): Add option --extra-digest-algo.
* gpgsm.h (struct): Add EXTRA_DIGEST_ALGO.
* verify.c (gpgsm_verify): Use it. Use the hash algorithm from
the signature value.
2007-12-11 Werner Koch <wk@g10code.com>
* certchain.c (do_validate_chain): Log AUDIT_ROOT_TRUSTED.

View File

@ -174,6 +174,7 @@ enum cmd_and_opt_values {
oOpenPGP,
oCipherAlgo,
oDigestAlgo,
oExtraDigestAlgo,
oCompressAlgo,
oCommandFD,
oNoVerbose,
@ -388,6 +389,7 @@ static ARGPARSE_OPTS opts[] = {
{ oCipherAlgo, "cipher-algo", 2 , N_("|NAME|use cipher algorithm NAME")},
{ oDigestAlgo, "digest-algo", 2 ,
N_("|NAME|use message digest algorithm NAME")},
{ oExtraDigestAlgo, "extra-digest-algo", 2 , "@" },
#if 0
{ oCompressAlgo, "compress-algo", 1 , N_("|N|use compress algorithm N")},
#endif
@ -842,6 +844,7 @@ main ( int argc, char **argv)
int use_random_seed = 1;
int with_fpr = 0;
char *def_digest_string = NULL;
char *extra_digest_algo = NULL;
enum cmd_and_opt_values cmd = 0;
struct server_control_s ctrl;
certlist_t recplist = NULL;
@ -1298,6 +1301,10 @@ main ( int argc, char **argv)
}
break;
case oExtraDigestAlgo:
extra_digest_algo = pargs.r.ret_str;
break;
case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
case oNoRandomSeedFile: use_random_seed = 0; break;
@ -1441,6 +1448,12 @@ main ( int argc, char **argv)
if (our_md_test_algo(opt.def_digest_algo) )
log_error (_("selected digest algorithm is invalid\n"));
}
if (extra_digest_algo)
{
opt.extra_digest_algo = gcry_md_map_name (extra_digest_algo);
if (our_md_test_algo (opt.extra_digest_algo) )
log_error (_("selected digest algorithm is invalid\n"));
}
}
if (log_get_errorcount(0))

View File

@ -92,6 +92,9 @@ struct
char *local_user; /* NULL or argument to -u */
int extra_digest_algo; /* A digest algorithm also used for
verification of signatures. */
int always_trust; /* Trust the given keys even if there is no
valid certification chain */
int skip_verify; /* do not check signatures on data */

View File

@ -491,7 +491,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
unsigned char *digest;
size_t digest_len;
/* Fixme do this for all signers and get the algo to use from
the signer's certificate - does not make mich sense, but we
the signer's certificate - does not make much sense, but we
should do this consistent as we have already done it above. */
algo = GCRY_MD_SHA1;
hash_data (data_fd, data_md);
@ -530,7 +530,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
}
/* We need to write at least a minimal list of our capabilities to
try to convince some MUAs to use 3DEs and not the crippled
try to convince some MUAs to use 3DES and not the crippled
RC2. Our list is:
aes128-CBC

View File

@ -203,10 +203,20 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
}
else
{
if (DBG_X509)
log_debug ("enabling hash algorithm %d (%s)\n",
algo, algoid? algoid:"");
gcry_md_enable (data_md, algo);
audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo);
}
}
if (opt.extra_digest_algo)
{
if (DBG_X509)
log_debug ("enabling extra hash algorithm %d\n",
opt.extra_digest_algo);
gcry_md_enable (data_md, opt.extra_digest_algo);
}
if (is_detached)
{
if (data_fd == -1)
@ -271,6 +281,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
char *msgdigest = NULL;
size_t msgdigestlen;
char *ctattr;
int sigval_hash_algo;
int info_pkalgo;
unsigned int verifyflags;
@ -331,7 +342,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
&algo, &is_enabled)
|| !is_enabled)
{
log_error ("digest algo %d has not been enabled\n", algo);
log_error ("digest algo %d (%s) has not been enabled\n",
algo, algoid?algoid:"");
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "unsupported");
goto next_signer;
}
@ -389,8 +401,16 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad");
goto next_signer;
}
sigval_hash_algo = hash_algo_from_sigval (sigval);
if (DBG_X509)
log_debug ("signer %d - signature available", signer);
{
log_debug ("signer %d - signature available (sigval hash=%d)",
signer, sigval_hash_algo);
/* log_printhex ("sigval ", sigval, */
/* gcry_sexp_canon_len (sigval, 0, NULL, NULL)); */
}
if (!sigval_hash_algo)
sigval_hash_algo = algo; /* Fallback used e.g. with old libksba. */
/* Find the certificate of the signer */
keydb_search_reset (kh);
@ -438,8 +458,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
gcry_md_hd_t md;
unsigned char *s;
/* check that the message digest in the signed attributes
matches the one we calculated on the data */
/* Check that the message digest in the signed attributes
matches the one we calculated on the data. */
s = gcry_md_read (data_md, algo);
if ( !s || !msgdigestlen
|| gcry_md_get_algo_dlen (algo) != msgdigestlen
@ -456,7 +476,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
goto next_signer;
}
rc = gcry_md_open (&md, algo, 0);
rc = gcry_md_open (&md, sigval_hash_algo, 0);
if (rc)
{
log_error ("md_open failed: %s\n", gpg_strerror (rc));
@ -476,14 +496,14 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp)
audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error");
goto next_signer;
}
rc = gpgsm_check_cms_signature (cert, sigval, md, algo,
&info_pkalgo);
rc = gpgsm_check_cms_signature (cert, sigval, md,
sigval_hash_algo, &info_pkalgo);
gcry_md_close (md);
}
else
{
rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo,
&info_pkalgo);
rc = gpgsm_check_cms_signature (cert, sigval, data_md,
algo, &info_pkalgo);
}
if (rc)