diff --git a/g10/misc.c b/g10/misc.c index 5325faab9..b0e5e2ce1 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -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 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 - address verbatim as utf-8 into the user ID under the assumtiopn - that mail programs etc handle IDNA at a lower level and take - OpenPGP 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 - native encoding and native to utf-8 encoding is done only after - checking. */ + address verbatim as utf-8 into the user ID under the assumption + that mail programs handle IDNA at a lower level and take OpenPGP + user IDs as utf-8. Note that we can't do an utf-8 encoding + checking here because in keygen.c this function is called with the + native encoding and native to utf-8 encoding is only done later. */ int has_invalid_email_chars (const char *s) { diff --git a/scd/ChangeLog b/scd/ChangeLog index c93a17a2b..937010d95 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,18 @@ +2009-01-08 Werner Koch + + * 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 * apdu.c (apdu_get_status): Save the last status. diff --git a/scd/apdu.c b/scd/apdu.c index f92565d7d..b3c0f66db 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -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 command chaining or extended length will be used according to these 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. - 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. 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 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 status and return all APDUs concatenated with one status word at 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 - log_info ("apdu_send_sdirect(%d) " + log_info ("apdu_send_direct(%d) " "got unexpected status %04X from get response\n", slot, sw); } @@ -3268,8 +3268,8 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen, unlock_slot (slot); - /* Append the status word - we reseved the two extra bytes while - allocating the buffer. */ + /* Append the status word. Note that we reserved the two extra + bytes while allocating the buffer. */ if (retbuf) { (*retbuf)[(*retbuflen)++] = (sw >> 8); diff --git a/scd/app-nks.c b/scd/app-nks.c index 31ddae0bd..edc179b5f 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -1,5 +1,5 @@ -/* app-nks.c - The Telesec NKS 2.0 card application. - * Copyright (C) 2004, 2007, 2008 Free Software Foundation, Inc. +/* app-nks.c - The Telesec NKS card application. + * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -34,29 +34,55 @@ static struct { 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 iskeypair; /* If true has the FID of the correspoding certificate. */ int issignkey; /* True if file is a key usable for signing. */ int isenckey; /* True if file is a key usable for decryption. */ } filelist[] = { - { 0x4531, 0, 0xC000, 1, 0 }, - { 0xC000, 101 }, - { 0x4331, 100 }, - { 0x4332, 100 }, - { 0xB000, 110 }, - { 0x45B1, 0, 0xC200, 0, 1 }, - { 0xC200, 101 }, - { 0x43B1, 100 }, - { 0x43B2, 100 }, - { 0, 0 } + { 0x4531, 0, 0, 0xC000, 1, 0 }, /* EF_PK.NKS.SIG */ + { 0xC000, 0, 101 }, /* EF_C.NKS.SIG */ + { 0x4331, 0, 100 }, + { 0x4332, 0, 100 }, + { 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */ + { 0x45B1, 0, 0, 0xC200, 0, 1 }, /* EF_PK.NKS.ENC */ + { 0xC200, 0, 101 }, /* EF_C.NKS.ENC */ + { 0x43B1, 0, 100 }, + { 0x43B2, 0, 100 }, + { 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 its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */ 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; unsigned char grip[20]; @@ -65,29 +91,34 @@ keygripstr_from_pk_file (int slot, int fid, char *r_gripstr) gcry_sexp_t sexp; int i; - err = iso7816_select_file (slot, fid, 0, NULL, NULL); + err = iso7816_select_file (app->slot, fid, 0, NULL, NULL); if (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) 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) { xfree (buffer[0]); 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 - Simple-TLV. We don't check the tag because the tests cards I - have use 1 for both, the modulus and the exponent - the - 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); + /* Old versions of NKS store the values in a TLV encoded format. + We need to do some checks. */ + for (i=0; i < 2; i++) + { + /* Check that the value appears like an integer encoded as + Simple-TLV. We don't check the tag because the tests cards I + have use 1 for both, the modulus and the exponent - the + 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) @@ -126,6 +157,9 @@ do_learn_status (app_t app, ctrl_t ctrl) /* Output information about all useful objects. */ for (i=0; filelist[i].fid; i++) { + if (filelist[i].nks_ver > app->app_local->nks_version) + continue; + if (filelist[i].certtype) { size_t len; @@ -149,7 +183,7 @@ do_learn_status (app_t app, ctrl_t ctrl) { 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) log_error ("can't get keygrip from FID 0x%04X: %s\n", 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 app_select_nks (app_t app) { @@ -593,6 +660,18 @@ app_select_nks (app_t app) { 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.readcert = do_readcert; app->fnc.getattr = NULL; @@ -605,6 +684,9 @@ app_select_nks (app_t app) app->fnc.check_pin = do_check_pin; } + leave: + if (rc) + do_deinit (app); return rc; } diff --git a/scd/command.c b/scd/command.c index 1037f1667..c535acbff 100644 --- a/scd/command.c +++ b/scd/command.c @@ -559,7 +559,7 @@ cmd_serialno (assuan_context_t ctx, char *line) 100 := Regular X.509 cert 101 := Trusted 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: diff --git a/scd/iso7816.c b/scd/iso7816.c index 2286090b6..ecb6dc1bd 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -1,5 +1,5 @@ /* 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. * @@ -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 the keypad. Returns 0 on success. */ gpg_error_t @@ -668,14 +710,7 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax, { buffer = NULL; bufferlen = 0; - /* Note, that we to set N to 254 due to problems either with the - 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; + n = read_all? 0 : nmax; sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, n, &buffer, &bufferlen); @@ -769,13 +804,11 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef, buffer = NULL; 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, recno, short_ef? short_ef : 0x04, -1, NULL, - 254, &buffer, &bufferlen); + 0, &buffer, &bufferlen); if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) { diff --git a/scd/iso7816.h b/scd/iso7816.h index 284751137..6c0485d0c 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -57,6 +57,10 @@ gpg_error_t iso7816_select_path (int slot, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_list_directory (int slot, int list_dirs, 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, iso7816_pininfo_t *pininfo); gpg_error_t iso7816_verify (int slot,