Add limited support for NetKey 3.0 cards.

This commit is contained in:
Werner Koch 2009-01-08 19:56:30 +00:00
parent 901842d522
commit 7784e86a66
7 changed files with 187 additions and 54 deletions

View File

@ -1248,17 +1248,16 @@ unescape_percent_string (const unsigned char *s)
} }
/* Check whether the string has characters not valid in an RFC822 /* Check whether the string has characters not valid in an RFC-822
address. To cope with OpenPGP we ignore allow non-ascii characters address. To cope with OpenPGP we ignore allow non-ascii characters
so that for example umlauts are legal in an email address. An so that for example umlauts are legal in an email address. An
OpenPGP user ID must be utf-8 encoded and tehre is no strict OpenPGP user ID must be utf-8 encoded but there is no strict
requirement for RFC-822. Thus to avoid IDNA encoding we put the requirement for RFC-822. Thus to avoid IDNA encoding we put the
address verbatim as utf-8 into the user ID under the assumtiopn address verbatim as utf-8 into the user ID under the assumption
that mail programs etc handle IDNA at a lower level and take that mail programs handle IDNA at a lower level and take OpenPGP
OpenPGP user IDS as utf-8. Note that we can't do an utf-8 encoding user IDs as utf-8. Note that we can't do an utf-8 encoding
checking here becuase in keygen.c this function is called with the checking here because in keygen.c this function is called with the
native encoding and native to utf-8 encoding is done only after native encoding and native to utf-8 encoding is only done later. */
checking. */
int int
has_invalid_email_chars (const char *s) has_invalid_email_chars (const char *s)
{ {

View File

@ -1,3 +1,18 @@
2009-01-08 Werner Koch <wk@g10code.com>
* iso7816.c (iso7816_read_record, iso7816_read_binary): Pass 0 for
L_e because the problem with the CCID driver has gone.
(iso7816_apdu_direct): New.
* app-nks.c (filelist): Add NKS_VER field. Add NKS 3 specific
entries.
(app_local_s, do_deinit): New.
(get_nks_version): New.
(app_select_nks): Setup local data.
(keygripstr_from_pk_file): Replace SLOT by APP and take care of
NKS version > 2.
(do_learn_status): Take care of NKS version.
2009-01-05 Werner Koch <wk@g10code.com> 2009-01-05 Werner Koch <wk@g10code.com>
* apdu.c (apdu_get_status): Save the last status. * apdu.c (apdu_get_status): Save the last status.

View File

@ -2793,9 +2793,9 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
related operations if not NULL. If EXTENDED_MODE is not NULL related operations if not NULL. If EXTENDED_MODE is not NULL
command chaining or extended length will be used according to these command chaining or extended length will be used according to these
values: values:
n < 0 := Use command chaining without the data part limited to -n n < 0 := Use command chaining with the data part limited to -n
in each chunk. If -1 is used a default value is used. in each chunk. If -1 is used a default value is used.
n == 1 := Use extended length for input and output with out a n == 1 := Use extended length for input and output without a
length limit. length limit.
n > 1 := Use extended length with up to N bytes. n > 1 := Use extended length with up to N bytes.
*/ */
@ -3107,7 +3107,7 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
/* This is a more generic version of the apdu sending routine. It /* This is a more generic version of the apdu sending routine. It
takes an already formatted APDU in APDUDATA or length APDUDATALEN takes an already formatted APDU in APDUDATA or length APDUDATALEN
and returns the with an APDU including the status word. With and returns with an APDU including the status word. With
HANDLE_MORE set to true this function will handle the MORE DATA HANDLE_MORE set to true this function will handle the MORE DATA
status and return all APDUs concatenated with one status word at status and return all APDUs concatenated with one status word at
the end. The function does not return a regular status word but 0 the end. The function does not return a regular status word but 0
@ -3237,7 +3237,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
} }
} }
else else
log_info ("apdu_send_sdirect(%d) " log_info ("apdu_send_direct(%d) "
"got unexpected status %04X from get response\n", "got unexpected status %04X from get response\n",
slot, sw); slot, sw);
} }
@ -3268,8 +3268,8 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
unlock_slot (slot); unlock_slot (slot);
/* Append the status word - we reseved the two extra bytes while /* Append the status word. Note that we reserved the two extra
allocating the buffer. */ bytes while allocating the buffer. */
if (retbuf) if (retbuf)
{ {
(*retbuf)[(*retbuflen)++] = (sw >> 8); (*retbuf)[(*retbuflen)++] = (sw >> 8);

View File

@ -1,5 +1,5 @@
/* app-nks.c - The Telesec NKS 2.0 card application. /* app-nks.c - The Telesec NKS card application.
* Copyright (C) 2004, 2007, 2008 Free Software Foundation, Inc. * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -34,29 +34,55 @@
static struct static struct
{ {
int fid; /* File ID. */ int fid; /* File ID. */
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 correspoding 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. */
} filelist[] = { } filelist[] = {
{ 0x4531, 0, 0xC000, 1, 0 }, { 0x4531, 0, 0, 0xC000, 1, 0 }, /* EF_PK.NKS.SIG */
{ 0xC000, 101 }, { 0xC000, 0, 101 }, /* EF_C.NKS.SIG */
{ 0x4331, 100 }, { 0x4331, 0, 100 },
{ 0x4332, 100 }, { 0x4332, 0, 100 },
{ 0xB000, 110 }, { 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */
{ 0x45B1, 0, 0xC200, 0, 1 }, { 0x45B1, 0, 0, 0xC200, 0, 1 }, /* EF_PK.NKS.ENC */
{ 0xC200, 101 }, { 0xC200, 0, 101 }, /* EF_C.NKS.ENC */
{ 0x43B1, 100 }, { 0x43B1, 0, 100 },
{ 0x43B2, 100 }, { 0x43B2, 0, 100 },
{ 0, 0 } { 0x4571, 3, 0, 0xc500, 0, 0 }, /* EF_PK.NKS.AUT */
{ 0xC500, 3, 101 }, /* EF_C.NKS.AUT */
{ 0x45B2, 3, 0, 0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */
{ 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */
{ 0 }
}; };
/* Object with application (i.e. NKS) specific data. */
struct app_local_s {
int nks_version; /* NKS version. */
};
/* Release local data. */
static void
do_deinit (app_t app)
{
if (app && app->app_local)
{
xfree (app->app_local);
app->app_local = NULL;
}
}
/* Read the file with FID, assume it contains a public key and return /* Read the file with FID, assume it contains a public key and return
its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */ its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */
static gpg_error_t static gpg_error_t
keygripstr_from_pk_file (int slot, int fid, char *r_gripstr) keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr)
{ {
gpg_error_t err; gpg_error_t err;
unsigned char grip[20]; unsigned char grip[20];
@ -65,29 +91,34 @@ keygripstr_from_pk_file (int slot, int fid, char *r_gripstr)
gcry_sexp_t sexp; gcry_sexp_t sexp;
int i; int i;
err = iso7816_select_file (slot, fid, 0, NULL, NULL); err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
if (err) if (err)
return err; return err;
err = iso7816_read_record (slot, 1, 1, 0, &buffer[0], &buflen[0]); err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]);
if (err) if (err)
return err; return err;
err = iso7816_read_record (slot, 2, 1, 0, &buffer[1], &buflen[1]); err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]);
if (err) if (err)
{ {
xfree (buffer[0]); xfree (buffer[0]);
return err; return err;
} }
for (i=0; i < 2; i++) if (app->app_local->nks_version < 3)
{ {
/* Check that the value appears like an integer encoded as /* Old versions of NKS store the values in a TLV encoded format.
Simple-TLV. We don't check the tag because the tests cards I We need to do some checks. */
have use 1 for both, the modulus and the exponent - the for (i=0; i < 2; i++)
example in the documentation gives 2 for the exponent. */ {
if (buflen[i] < 3) /* Check that the value appears like an integer encoded as
err = gpg_error (GPG_ERR_TOO_SHORT); Simple-TLV. We don't check the tag because the tests cards I
else if (buffer[i][1] != buflen[i]-2 ) have use 1 for both, the modulus and the exponent - the
err = gpg_error (GPG_ERR_INV_OBJ); example in the documentation gives 2 for the exponent. */
if (buflen[i] < 3)
err = gpg_error (GPG_ERR_TOO_SHORT);
else if (buffer[i][1] != buflen[i]-2 )
err = gpg_error (GPG_ERR_INV_OBJ);
}
} }
if (!err) if (!err)
@ -126,6 +157,9 @@ do_learn_status (app_t app, ctrl_t ctrl)
/* Output information about all useful objects. */ /* Output information about all useful objects. */
for (i=0; filelist[i].fid; i++) for (i=0; filelist[i].fid; i++)
{ {
if (filelist[i].nks_ver > app->app_local->nks_version)
continue;
if (filelist[i].certtype) if (filelist[i].certtype)
{ {
size_t len; size_t len;
@ -149,7 +183,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
{ {
char gripstr[40+1]; char gripstr[40+1];
err = keygripstr_from_pk_file (app->slot, filelist[i].fid, gripstr); err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr);
if (err) if (err)
log_error ("can't get keygrip from FID 0x%04X: %s\n", log_error ("can't get keygrip from FID 0x%04X: %s\n",
filelist[i].fid, gpg_strerror (err)); filelist[i].fid, gpg_strerror (err));
@ -580,7 +614,40 @@ do_check_pin (app_t app, const char *keyidstr,
} }
/* Select the NKS 2.0 application. */ /* Return the version of the NKS application. */
static int
get_nks_version (int slot)
{
unsigned char *result = NULL;
size_t resultlen;
int type;
if (iso7816_apdu_direct (slot, "\x80\xaa\x06\x00\x00", 5, 0,
&result, &resultlen))
return 2; /* NKS 2 does not support this command. */
/* Example value: 04 11 19 22 21 6A 20 80 03 03 01 01 01 00 00 00
vv tt ccccccccccccccccc aa bb cc vvvvvvvvvvv xx
vendor (Philips) -+ | | | | | | |
chip type -----------+ | | | | | |
chip id ----------------+ | | | | |
card type (3 - tcos 3) -------------------+ | | | |
OS version of card type ---------------------+ | | |
OS release of card type ------------------------+ | |
OS vendor internal version ------------------------+ |
RFU -----------------------------------------------------------+
*/
if (resultlen < 16)
type = 0; /* Invalid data returned. */
else
type = result[8];
xfree (result);
return type;
}
/* Select the NKS application. */
gpg_error_t gpg_error_t
app_select_nks (app_t app) app_select_nks (app_t app)
{ {
@ -593,6 +660,18 @@ app_select_nks (app_t app)
{ {
app->apptype = "NKS"; app->apptype = "NKS";
app->app_local = xtrycalloc (1, sizeof *app->app_local);
if (!app->app_local)
{
rc = gpg_error (gpg_err_code_from_errno (errno));
goto leave;
}
app->app_local->nks_version = get_nks_version (slot);
if (opt.verbose)
log_info ("Detected NKS version: %d\n", app->app_local->nks_version);
app->fnc.deinit = do_deinit;
app->fnc.learn_status = do_learn_status; app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert; app->fnc.readcert = do_readcert;
app->fnc.getattr = NULL; app->fnc.getattr = NULL;
@ -605,6 +684,9 @@ app_select_nks (app_t app)
app->fnc.check_pin = do_check_pin; app->fnc.check_pin = do_check_pin;
} }
leave:
if (rc)
do_deinit (app);
return rc; return rc;
} }

View File

@ -559,7 +559,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
100 := Regular X.509 cert 100 := Regular X.509 cert
101 := Trusted X.509 cert 101 := Trusted X.509 cert
102 := Useful X.509 cert 102 := Useful X.509 cert
110 := Root CA cert (DINSIG) 110 := Root CA cert (e.g. DINSIG)
For certain cards, more information will be returned: For certain cards, more information will be returned:

View File

@ -1,5 +1,5 @@
/* iso7816.c - ISO 7816 commands /* iso7816.c - ISO 7816 commands
* Copyright (C) 2003, 2004, 2008 Free Software Foundation, Inc. * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -226,6 +226,48 @@ iso7816_list_directory (int slot, int list_dirs,
} }
/* This funcion sends an already formatted APDU to the card. With
HANDLE_MORE set to true a MORE DATA status will be handled
internally. The return value is a gpg error code (i.e. a mapped
status word). This is basically the same as apdu_send_direct but
it maps the status word and does not return it in the result
buffer. */
gpg_error_t
iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
int handle_more,
unsigned char **result, size_t *resultlen)
{
int sw;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
sw = apdu_send_direct (slot, apdudata, apdudatalen, handle_more,
result, resultlen);
if (!sw)
{
if (*resultlen < 2)
sw = SW_HOST_GENERAL_ERROR;
else
{
sw = ((*result)[*resultlen-2] << 8) | (*result)[*resultlen-1];
(*resultlen)--;
(*resultlen)--;
}
}
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (*result);
*result = NULL;
*resultlen = 0;
}
return map_sw (sw);
}
/* Check whether the reader supports the ISO command code COMMAND on /* Check whether the reader supports the ISO command code COMMAND on
the keypad. Returns 0 on success. */ the keypad. Returns 0 on success. */
gpg_error_t gpg_error_t
@ -668,14 +710,7 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
{ {
buffer = NULL; buffer = NULL;
bufferlen = 0; bufferlen = 0;
/* Note, that we to set N to 254 due to problems either with the n = read_all? 0 : nmax;
ccid driver or some TCOS cards. It actually should be 0
which is the official ISO value to read a variable length
object. */
if (read_all || nmax > 254)
n = 254;
else
n = nmax;
sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
n, &buffer, &bufferlen); n, &buffer, &bufferlen);
@ -769,13 +804,11 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
buffer = NULL; buffer = NULL;
bufferlen = 0; bufferlen = 0;
/* Fixme: Either the ccid driver or the TCOS cards have problems
with an Le of 0. */
sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD, sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
recno, recno,
short_ef? short_ef : 0x04, short_ef? short_ef : 0x04,
-1, NULL, -1, NULL,
254, &buffer, &bufferlen); 0, &buffer, &bufferlen);
if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
{ {

View File

@ -57,6 +57,10 @@ gpg_error_t iso7816_select_path (int slot,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_list_directory (int slot, int list_dirs, gpg_error_t iso7816_list_directory (int slot, int list_dirs,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_apdu_direct (int slot,
const void *apdudata, size_t apdudatalen,
int handle_more,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_check_keypad (int slot, int command, gpg_error_t iso7816_check_keypad (int slot, int command,
iso7816_pininfo_t *pininfo); iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_verify (int slot, gpg_error_t iso7816_verify (int slot,