Signing using Netkey 3 cards does now work.

This commit is contained in:
Werner Koch 2009-03-26 19:27:04 +00:00
parent 6e63e54b00
commit 990585ad7d
14 changed files with 153 additions and 49 deletions

2
NEWS
View File

@ -17,6 +17,8 @@ Noteworthy changes in version 2.0.12 (not released)
* Better synchronization of several smartcard sessions. * Better synchronization of several smartcard sessions.
* Support for the Telesec Netkey 3 cards.
Noteworthy changes in version 2.0.11 (2009-03-03) Noteworthy changes in version 2.0.11 (2009-03-03)
------------------------------------------------- -------------------------------------------------

View File

@ -1,5 +1,9 @@
2009-03-26 Werner Koch <wk@g10code.com> 2009-03-26 Werner Koch <wk@g10code.com>
* agent.h (MAX_DIGEST_LEN): Change to 64.
* command.c (cmd_sethash): Allow digest length of 48 and 64.
(cmd_sethash): Allow more hash algos.
* trustlist.c (reformat_name): New. * trustlist.c (reformat_name): New.
(agent_marktrusted): Use a reformatted name. Reload the table (agent_marktrusted): Use a reformatted name. Reload the table
before the update and always reload it at the end. before the update and always reload it at the end.

View File

@ -42,7 +42,7 @@
#define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1) #define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1)
/* Maximum length of a digest. */ /* Maximum length of a digest. */
#define MAX_DIGEST_LEN 36 #define MAX_DIGEST_LEN 64
/* A large struct name "opt" to keep global flags */ /* A large struct name "opt" to keep global flags */
struct struct

View File

@ -637,8 +637,14 @@ cmd_sethash (assuan_context_t ctx, char *line)
{ {
if (has_option (line, "--hash=sha1")) if (has_option (line, "--hash=sha1"))
algo = GCRY_MD_SHA1; algo = GCRY_MD_SHA1;
else if (has_option (line, "--hash=sha224"))
algo = GCRY_MD_SHA224;
else if (has_option (line, "--hash=sha256")) else if (has_option (line, "--hash=sha256"))
algo = GCRY_MD_SHA256; algo = GCRY_MD_SHA256;
else if (has_option (line, "--hash=sha384"))
algo = GCRY_MD_SHA384;
else if (has_option (line, "--hash=sha512"))
algo = GCRY_MD_SHA512;
else if (has_option (line, "--hash=rmd160")) else if (has_option (line, "--hash=rmd160"))
algo = GCRY_MD_RMD160; algo = GCRY_MD_RMD160;
else if (has_option (line, "--hash=md5")) else if (has_option (line, "--hash=md5"))
@ -671,7 +677,8 @@ cmd_sethash (assuan_context_t ctx, char *line)
n /= 2; n /= 2;
if (algo == MD_USER_TLS_MD5SHA1 && n == 36) if (algo == MD_USER_TLS_MD5SHA1 && n == 36)
; ;
else if (n != 16 && n != 20 && n != 24 && n != 32) else if (n != 16 && n != 20 && n != 24
&& n != 28 && n != 32 && n != 48 && n != 64)
return set_error (GPG_ERR_ASS_PARAMETER, "unsupported length of hash"); return set_error (GPG_ERR_ASS_PARAMETER, "unsupported length of hash");
if (n > MAX_DIGEST_LEN) if (n > MAX_DIGEST_LEN)

View File

@ -568,6 +568,12 @@ encryption. For convenience the strings @code{3DES}, @code{AES} and
@code{AES256} may be used instead of their OIDs. The default is @code{AES256} may be used instead of their OIDs. The default is
@code{3DES} (1.2.840.113549.3.7). @code{3DES} (1.2.840.113549.3.7).
@item --digest-algo @code{name}
Use @code{name} as the message digest algorithm. Usually this
algorithm is deduced from the respective signing certificate. This
option forces the use of the given algorithm and may lead to severe
interoperability problems.
@end table @end table

View File

@ -1,3 +1,16 @@
2009-03-26 Werner Koch <wk@g10code.com>
* command.c (cmd_pksign): Allow more hash algorithms.
* scdaemon.h (MAX_DIGEST_LEN): Change to 64.
* apdu.c (open_ccid_reader): Clear the is_to flag.
* app-nks.c (filelist): Add field KID.
(do_getattr): Change standard authentication key.
(do_sign): Setup a security environment for TCOS 3 cards and support
all SHA-2 algorithms.
2009-03-24 Werner Koch <wk@g10code.com> 2009-03-24 Werner Koch <wk@g10code.com>
* command.c (struct server_local_s): Add flag * command.c (struct server_local_s): Add flag

View File

@ -1951,6 +1951,9 @@ open_ccid_reader (const char *portstr)
reader_table[slot].send_apdu_reader = send_apdu_ccid; reader_table[slot].send_apdu_reader = send_apdu_ccid;
reader_table[slot].check_keypad = check_ccid_keypad; reader_table[slot].check_keypad = check_ccid_keypad;
reader_table[slot].dump_status_reader = dump_ccid_reader_status; reader_table[slot].dump_status_reader = dump_ccid_reader_status;
/* Our CCID reader code does not support T=0 at all, thus reset the
flag. */
reader_table[slot].is_t0 = 0;
dump_reader_status (slot); dump_reader_status (slot);
return slot; return slot;
@ -2839,10 +2842,10 @@ send_le (int slot, int class, int ins, int p0, int p1,
if (lc != -1 && (lc > 255 || lc < 0)) if (lc != -1 && (lc > 255 || lc < 0))
{ {
/* Data does not fit into an APDU. What we do now dependes on /* Data does not fit into an APDU. What we do now depends on
the EXTENDED_MODE parameter. */ the EXTENDED_MODE parameter. */
if (!extended_mode) if (!extended_mode)
return SW_WRONG_LENGTH; /* No way. to send such an APDU. */ return SW_WRONG_LENGTH; /* No way to send such an APDU. */
else if (extended_mode > 0) else if (extended_mode > 0)
return SW_HOST_NOT_SUPPORTED; /* FIXME. */ return SW_HOST_NOT_SUPPORTED; /* FIXME. */
else if (extended_mode < 0) else if (extended_mode < 0)

View File

@ -19,10 +19,11 @@
/* Notes: /* Notes:
- This is still work in progress. We are now targeting TCOS 3 cards - We are now targeting TCOS 3 cards and it may happen that there is
but try to keep compatibility to TCOS 2. Both are not fully a regression towards TCOS 2 cards. Please report.
working as of now. TCOS 3 PIN management seems to work. Use GPA
from SVN trunk to test it. - The TKS3 AUT key is not used by our authentication command but
accessible via the decrypt command.
- If required, we automagically switch between the NKS application - If required, we automagically switch between the NKS application
and the SigG application. This avoids to use the DINSIG and the SigG application. This avoids to use the DINSIG
@ -63,25 +64,26 @@ static struct
int fid; /* File ID. */ int fid; /* File ID. */
int nks_ver; /* 0 for NKS version 2, 3 for version 3. */ int nks_ver; /* 0 for NKS version 2, 3 for version 3. */
int certtype; /* Type of certificate or 0 if it is not a certificate. */ int certtype; /* Type of certificate or 0 if it is not a certificate. */
int iskeypair; /* If true has the FID of the correspoding certificate. */ int iskeypair; /* If true has the FID of the corresponding certificate. */
int issignkey; /* True if file is a key usable for signing. */ int issignkey; /* True if file is a key usable for signing. */
int isenckey; /* True if file is a key usable for decryption. */ int isenckey; /* True if file is a key usable for decryption. */
unsigned char kid; /* Corresponding key references. */
} filelist[] = { } filelist[] = {
{ 0, 0x4531, 0, 0, 0xC000, 1, 0 }, /* EF_PK.NKS.SIG */ { 0, 0x4531, 0, 0, 0xC000, 1, 0, 0x80 }, /* EF_PK.NKS.SIG */
{ 1, 0x4531, 3, 0, 0x0000, 1, 1 }, /* EF_PK.CH.SIG */ { 1, 0x4531, 3, 0, 0x0000, 1, 1, 0x84 }, /* EF_PK.CH.SIG */
{ 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */ { 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */
{ 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */ { 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */
{ 0, 0x4331, 0, 100 }, { 0, 0x4331, 0, 100 },
{ 0, 0x4332, 0, 100 }, { 0, 0x4332, 0, 100 },
{ 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */ { 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */
{ 0, 0x45B1, 0, 0, 0xC200, 0, 1 }, /* EF_PK.NKS.ENC */ { 0, 0x45B1, 0, 0, 0xC200, 0, 1, 0x81 }, /* EF_PK.NKS.ENC */
{ 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */ { 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */
{ 0, 0x43B1, 0, 100 }, { 0, 0x43B1, 0, 100 },
{ 0, 0x43B2, 0, 100 }, { 0, 0x43B2, 0, 100 },
{ 0, 0x4571, 3, 0, 0xc500, 0, 0 }, /* EF_PK.NKS.AUT */ { 0, 0x4571, 3, 0, 0xc500, 0, 0, 0x82 }, /* EF_PK.NKS.AUT */
{ 0, 0xC500, 3, 101 }, /* EF_C.NKS.AUT */ { 0, 0xC500, 3, 101 }, /* EF_C.NKS.AUT */
{ 0, 0x45B2, 3, 0, 0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */ { 0, 0x45B2, 3, 0, 0xC201, 0, 1, 0x83 }, /* EF_PK.NKS.ENC1024 */
{ 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */ { 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */
/* { 1, 0xB000, 3, ... */ /* { 1, 0xB000, 3, ... */
{ 0, 0 } { 0, 0 }
}; };
@ -303,10 +305,12 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
{ {
case 1: /* $AUTHKEYID */ case 1: /* $AUTHKEYID */
{ {
/* NetKey 3.0 cards define this key for authentication. /* NetKey 3.0 cards define an authentication key but according
FIXME: We don't have the readkey command, so this to the specs this key is only usable for encryption and not
information is pretty useless. */ signing. it might work anyway but it has not yet been
char const tmp[] = "NKS-NKS3.4571"; tested - fixme. Thus for now we use the NKS signature key
for authentication. */
char const tmp[] = "NKS-NKS3.4531";
send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0); send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
} }
break; break;
@ -685,13 +689,18 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
int rc, i; int rc, i;
int is_sigg = 0; int is_sigg = 0;
int fid; int fid;
unsigned char data[35]; /* Must be large enough for a SHA-1 digest unsigned char kid;
+ the largest OID _prefix above. */ unsigned char data[83]; /* Must be large enough for a SHA-1 digest
+ the largest OID prefix. */
size_t datalen;
if (!keyidstr || !*keyidstr) if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
if (indatalen != 20 && indatalen != 16 && indatalen != 35) switch (indatalen)
return gpg_error (GPG_ERR_INV_VALUE); {
case 16: case 20: case 35: case 47: case 51: case 67: case 83: break;
default: return gpg_error (GPG_ERR_INV_VALUE);
}
/* Check that the provided ID is valid. This is not really needed /* Check that the provided ID is valid. This is not really needed
but we do it to enforce correct usage by the caller. */ but we do it to enforce correct usage by the caller. */
@ -721,22 +730,35 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
return gpg_error (GPG_ERR_NOT_FOUND); return gpg_error (GPG_ERR_NOT_FOUND);
if (!filelist[i].issignkey) if (!filelist[i].issignkey)
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
kid = filelist[i].kid;
/* Prepare the DER object from INDATA. */ /* Prepare the DER object from INDATA. */
if (indatalen == 35) if (app->app_local->nks_version > 2 && (indatalen == 35
|| indatalen == 47
|| indatalen == 51
|| indatalen == 67
|| indatalen == 83))
{
/* The caller send data matching the length of the ASN.1 encoded
hash for SHA-{1,224,256,384,512}. Assume that is okay. */
assert (indatalen <= sizeof data);
memcpy (data, indata, indatalen);
datalen = indatalen;
}
else if (indatalen == 35)
{ {
/* Alright, the caller was so kind to send us an already /* Alright, the caller was so kind to send us an already
prepared DER object. Check that it is waht we want and that prepared DER object. This is for TCOS 2. */
it matches the hash algorithm. */
if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15)) if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
; ;
else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15)) else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata,rmd160_prefix,15))
; ;
else else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
memcpy (data, indata, indatalen); memcpy (data, indata, indatalen);
datalen = 35;
} }
else else if (indatalen == 20)
{ {
if (hashalgo == GCRY_MD_SHA1) if (hashalgo == GCRY_MD_SHA1)
memcpy (data, sha1_prefix, 15); memcpy (data, sha1_prefix, 15);
@ -745,11 +767,32 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
else else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
memcpy (data+15, indata, indatalen); memcpy (data+15, indata, indatalen);
datalen = 35;
} }
else
return gpg_error (GPG_ERR_INV_VALUE);
rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
/* Send an MSE for PSO:Computer_Signature. */
if (app->app_local->nks_version > 2)
{
unsigned char mse[6];
mse[0] = 0x80; /* Algorithm reference. */
mse[1] = 1;
mse[2] = 2; /* RSA, card does pkcs#1 v1.5 padding, no ASN.1 check. */
mse[3] = 0x84; /* Private key reference. */
mse[4] = 1;
mse[5] = kid;
rc = iso7816_manage_security_env (app->slot, 0x41, 0xB6,
mse, sizeof mse);
}
/* Verify using PW1.CH. */
if (!rc) if (!rc)
rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen); rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
/* Compute the signature. */
if (!rc)
rc = iso7816_compute_ds (app->slot, data, datalen, outdata, outdatalen);
return rc; return rc;
} }

View File

@ -905,7 +905,7 @@ pin_cb (void *opaque, const char *info, char **retstr)
} }
/* PKSIGN [--hash=[rmd160|sha1|md5]] <hexified_id> /* PKSIGN [--hash=[rmd160|sha{1,224,256,384,512}|md5]] <hexified_id>
The --hash option is optional; the default is SHA1. The --hash option is optional; the default is SHA1.
@ -924,6 +924,14 @@ cmd_pksign (assuan_context_t ctx, char *line)
hash_algo = GCRY_MD_RMD160; hash_algo = GCRY_MD_RMD160;
else if (has_option (line, "--hash=sha1")) else if (has_option (line, "--hash=sha1"))
hash_algo = GCRY_MD_SHA1; hash_algo = GCRY_MD_SHA1;
else if (has_option (line, "--hash=sha224"))
hash_algo = GCRY_MD_SHA224;
else if (has_option (line, "--hash=sha256"))
hash_algo = GCRY_MD_SHA256;
else if (has_option (line, "--hash=sha384"))
hash_algo = GCRY_MD_SHA384;
else if (has_option (line, "--hash=sha512"))
hash_algo = GCRY_MD_SHA512;
else if (has_option (line, "--hash=md5")) else if (has_option (line, "--hash=md5"))
hash_algo = GCRY_MD_MD5; hash_algo = GCRY_MD_MD5;
else if (!strstr (line, "--")) else if (!strstr (line, "--"))

View File

@ -39,7 +39,7 @@
#define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1) #define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1)
/* Maximum length of a digest. */ /* Maximum length of a digest. */
#define MAX_DIGEST_LEN 36 #define MAX_DIGEST_LEN 64

View File

@ -1,5 +1,12 @@
2009-03-26 Werner Koch <wk@g10code.com> 2009-03-26 Werner Koch <wk@g10code.com>
* gpgsm.c (main): s/def_digest_string/forced_digest_algo/ and
activate the --digest-algo option.
* gpgsm.h (struct opt): s/def_digest_algo/forced_digest_algo/.
* sign.c (gpgsm_sign): Implement --digest-algo.
* sign.c (MAX_DIGEST_LEN): Change to 64.
* call-agent.c (gpgsm_agent_marktrusted): Format the issuer name. * call-agent.c (gpgsm_agent_marktrusted): Format the issuer name.
2009-03-25 Werner Koch <wk@g10code.com> 2009-03-25 Werner Koch <wk@g10code.com>

View File

@ -843,8 +843,8 @@ main ( int argc, char **argv)
int use_random_seed = 1; int use_random_seed = 1;
int no_common_certs_import = 0; int no_common_certs_import = 0;
int with_fpr = 0; int with_fpr = 0;
char *def_digest_string = NULL; const char *forced_digest_algo = NULL;
char *extra_digest_algo = NULL; const char *extra_digest_algo = NULL;
enum cmd_and_opt_values cmd = 0; enum cmd_and_opt_values cmd = 0;
struct server_control_s ctrl; struct server_control_s ctrl;
certlist_t recplist = NULL; certlist_t recplist = NULL;
@ -1301,7 +1301,7 @@ main ( int argc, char **argv)
break; break;
case oDigestAlgo: case oDigestAlgo:
/* Dummy for now. */ forced_digest_algo = pargs.r.ret_str;
break; break;
case oExtraDigestAlgo: case oExtraDigestAlgo:
@ -1460,12 +1460,10 @@ main ( int argc, char **argv)
|| !gcry_cipher_mode_from_oid (opt.def_cipher_algoid)) || !gcry_cipher_mode_from_oid (opt.def_cipher_algoid))
log_error (_("selected cipher algorithm is invalid\n")); log_error (_("selected cipher algorithm is invalid\n"));
if (def_digest_string) if (forced_digest_algo)
{ {
opt.def_digest_algo = gcry_md_map_name (def_digest_string); opt.forced_digest_algo = gcry_md_map_name (forced_digest_algo);
xfree (def_digest_string); if (our_md_test_algo(opt.forced_digest_algo) )
def_digest_string = NULL;
if (our_md_test_algo(opt.def_digest_algo) )
log_error (_("selected digest algorithm is invalid\n")); log_error (_("selected digest algorithm is invalid\n"));
} }
if (extra_digest_algo) if (extra_digest_algo)

View File

@ -33,7 +33,7 @@
#include "../common/estream.h" #include "../common/estream.h"
#include "../common/audit.h" #include "../common/audit.h"
#define MAX_DIGEST_LEN 24 #define MAX_DIGEST_LEN 64
struct keyserver_spec struct keyserver_spec
{ {
@ -92,9 +92,10 @@ struct
const char *def_cipher_algoid; /* cipher algorithm to use if const char *def_cipher_algoid; /* cipher algorithm to use if
nothing else is specified */ nothing else is specified */
int def_digest_algo; /* Ditto for hash algorithm */
int def_compress_algo; /* Ditto for compress algorithm */ int def_compress_algo; /* Ditto for compress algorithm */
int forced_digest_algo; /* User forced hash algorithm. */
char *def_recipient; /* userID of the default recipient */ char *def_recipient; /* userID of the default recipient */
int def_recipient_self; /* The default recipient is the default key */ int def_recipient_self; /* The default recipient is the default key */

View File

@ -399,11 +399,22 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
/* Figure out the hash algorithm to use. We do not want to use the /* Figure out the hash algorithm to use. We do not want to use the
one for the certificate but if possible an OID for the plain one for the certificate but if possible an OID for the plain
algorithm. */ algorithm. */
if (opt.forced_digest_algo && opt.verbose)
log_info ("user requested hash algorithm %d\n", opt.forced_digest_algo);
for (i=0, cl=signerlist; cl; cl = cl->next, i++) for (i=0, cl=signerlist; cl; cl = cl->next, i++)
{ {
const char *oid = ksba_cert_get_digest_algo (cl->cert); const char *oid = ksba_cert_get_digest_algo (cl->cert);
cl->hash_algo = oid ? gcry_md_map_name (oid) : 0; if (opt.forced_digest_algo)
{
oid = NULL;
cl->hash_algo = opt.forced_digest_algo;
}
else
{
oid = ksba_cert_get_digest_algo (cl->cert);
cl->hash_algo = oid ? gcry_md_map_name (oid) : 0;
}
switch (cl->hash_algo) switch (cl->hash_algo)
{ {
case GCRY_MD_SHA1: oid = "1.3.14.3.2.26"; break; case GCRY_MD_SHA1: oid = "1.3.14.3.2.26"; break;
@ -427,6 +438,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
} }
cl->hash_algo_oid = oid; cl->hash_algo_oid = oid;
} }
if (opt.verbose) if (opt.verbose)
{ {
for (i=0, cl=signerlist; cl; cl = cl->next, i++) for (i=0, cl=signerlist; cl; cl = cl->next, i++)