[svn] GnuPG - r4944 - in trunk: . agent doc scd
svn author wk
cvs at cvs.gnupg.org
Thu Mar 5 20:19:38 CET 2009
Author: wk
Date: 2009-03-05 20:19:37 +0100 (Thu, 05 Mar 2009)
New Revision: 4944
Modified:
trunk/NEWS
trunk/agent/ChangeLog
trunk/agent/command.c
trunk/agent/divert-scd.c
trunk/agent/findkey.c
trunk/configure.ac
trunk/doc/ChangeLog
trunk/doc/gpg.texi
trunk/doc/help.txt
trunk/scd/ChangeLog
trunk/scd/app-nks.c
trunk/scd/app-openpgp.c
trunk/scd/command.c
Log:
New PIN Callback attributes in gpg-agent.
Common prompts for keypad and simple card reader.
More support for Netkey cards; PIN management works now.
Modified: trunk/agent/ChangeLog
===================================================================
--- trunk/agent/ChangeLog 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/agent/ChangeLog 2009-03-05 19:19:37 UTC (rev 4944)
@@ -1,3 +1,9 @@
+2009-03-05 Werner Koch <wk at g10code.com>
+
+ * divert-scd.c (getpin_cb): Support flag 'P'. Change max_digits
+ from 8 to 16. Append a message about keypads.
+ * findkey.c (unprotect): Change max digits to 16.
+
2009-03-02 Werner Koch <wk at g10code.com>
* command.c (cmd_getinfo): Add subcommand "scd_running".
Modified: trunk/doc/ChangeLog
===================================================================
--- trunk/doc/ChangeLog 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/doc/ChangeLog 2009-03-05 19:19:37 UTC (rev 4944)
@@ -1,3 +1,12 @@
+2009-03-04 Werner Koch <wk at g10code.com>
+
+ * help.txt (gpg.keygen.size): Add a link to web page.
+
+2009-03-03 Werner Koch <wk at g10code.com>
+
+ * gpg.texi (Operational GPG Commands): "merge-only" is an
+ import-option. Reported by Joseph Oreste Bruni.
+
2009-03-02 Werner Koch <wk at g10code.com>
* gpg-agent.texi (Invoking GPG-AGENT): Modernized instructions.
Modified: trunk/scd/ChangeLog
===================================================================
--- trunk/scd/ChangeLog 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/scd/ChangeLog 2009-03-05 19:19:37 UTC (rev 4944)
@@ -1,3 +1,30 @@
+2009-03-05 Werner Koch <wk at g10code.com>
+
+ * app-openpgp.c (verify_a_chv): Remove special case for keypads.
+ (verify_chv3): Ditto.
+
+ * app-nks.c (get_chv_status): New.
+ (parse_pwidstr): New.
+ (verify_pin): Add args PWID and DESC and use them. Remove the
+ CHV1 caching.
+ (do_change_pin): Allow PIN selection and add reset mode.
+ (do_learn_status): Use NKS-NKS3 tag for TCOS 3 cards.
+ (do_readcert, do_sign): Allow NKS-NKS3 tag.
+
+2009-03-04 Werner Koch <wk at g10code.com>
+
+ * app-nks.c (do_getattr): New.
+ (app_select_nks): Register it.
+ (verify_pin): Factor some code out to...
+ (basic_pin_checks): New.
+ (do_change_pin): Call the basic check.
+ (app_select_nks): Move AID to ..
+ (aid_nks): .. new.
+ (aid_sigg): New.
+ (switch_application): New.
+ (do_getattr, do_learn_status, do_readcert, do_sign, do_decipher)
+ (do_change_pin, do_check_pin): Make sure we are in NKS mode.
+
2009-03-03 Werner Koch <wk at g10code.com>
* command.c (scd_command_handler): Remove dereference of STOPME
Modified: trunk/NEWS
===================================================================
--- trunk/NEWS 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/NEWS 2009-03-05 19:19:37 UTC (rev 4944)
@@ -1,3 +1,7 @@
+Noteworthy changes in version 2.0.12
+-------------------------------------------------
+
+
Noteworthy changes in version 2.0.11 (2009-03-03)
-------------------------------------------------
Modified: trunk/agent/command.c
===================================================================
--- trunk/agent/command.c 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/agent/command.c 2009-03-05 19:19:37 UTC (rev 4944)
@@ -1061,7 +1061,7 @@
/* PASSWD <hexstring_with_keygrip>
- Change the passphrase/PID for the key identified by keygrip in LINE. */
+ Change the passphrase/PIN for the key identified by keygrip in LINE. */
static int
cmd_passwd (assuan_context_t ctx, char *line)
{
Modified: trunk/agent/divert-scd.c
===================================================================
--- trunk/agent/divert-scd.c 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/agent/divert-scd.c 2009-03-05 19:19:37 UTC (rev 4944)
@@ -1,5 +1,5 @@
/* divert-scd.c - divert operations to the scdaemon
- * Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2003, 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -181,10 +181,11 @@
Flags:
- 'N' = New PIN, this requests a second prompt to repeat the the
+ 'N' = New PIN, this requests a second prompt to repeat the
PIN. If the PIN is not correctly repeated it starts from
all over.
- 'A' = The PIN is an Admin PIN, SO-PIN, PUK or alike.
+ 'A' = The PIN is an Admin PIN, SO-PIN or alike.
+ 'P' = The PIN is a PUK (Personal Unblocking Key).
'R' = The PIN is a Reset Code.
Example:
@@ -204,6 +205,7 @@
int any_flags = 0;
int newpin = 0;
int resetcode = 0;
+ int is_puk = 0;
const char *again_text = NULL;
const char *prompt = "PIN";
@@ -217,6 +219,13 @@
{
if (*s == 'A')
prompt = _("Admin PIN");
+ else if (*s == 'P')
+ {
+ /* TRANSLATORS: A PUK is the Personal Unblocking Code
+ used to unblock a PIN. */
+ prompt = _("PUK");
+ is_puk = 1;
+ }
else if (*s == 'N')
newpin = 1;
else if (*s == 'R')
@@ -242,7 +251,22 @@
}
else if (maxbuf == 1) /* Open the pinentry. */
{
- rc = agent_popup_message_start (ctrl, info, NULL);
+ if (info)
+ {
+ char *desc;
+
+ if ( asprintf (&desc,
+ _("%s%%0A%%0AUse the reader's keypad for input."),
+ info) < 0 )
+ rc = gpg_error_from_syserror ();
+ else
+ {
+ rc = agent_popup_message_start (ctrl, desc, NULL);
+ xfree (desc);
+ }
+ }
+ else
+ rc = agent_popup_message_start (ctrl, NULL, NULL);
}
else
rc = gpg_error (GPG_ERR_INV_VALUE);
@@ -258,7 +282,7 @@
return gpg_error_from_syserror ();
pi->max_length = maxbuf-1;
pi->min_digits = 0; /* we want a real passphrase */
- pi->max_digits = 8;
+ pi->max_digits = 16;
pi->max_tries = 3;
if (any_flags)
@@ -277,17 +301,21 @@
}
pi2->max_length = maxbuf-1;
pi2->min_digits = 0;
- pi2->max_digits = 8;
+ pi2->max_digits = 16;
pi2->max_tries = 1;
rc = agent_askpin (ctrl,
(resetcode?
_("Repeat this Reset Code"):
+ is_puk?
+ _("Repeat this PUK"):
_("Repeat this PIN")),
prompt, NULL, pi2);
if (!rc && strcmp (pi->pin, pi2->pin))
{
again_text = (resetcode?
N_("Reset Code not correctly repeated; try again"):
+ is_puk?
+ N_("PUK not correctly repeated; try again"):
N_("PIN not correctly repeated; try again"));
xfree (pi2);
xfree (pi);
Modified: trunk/agent/findkey.c
===================================================================
--- trunk/agent/findkey.c 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/agent/findkey.c 2009-03-05 19:19:37 UTC (rev 4944)
@@ -367,7 +367,7 @@
return gpg_error_from_syserror ();
pi->max_length = 100;
pi->min_digits = 0; /* we want a real passphrase */
- pi->max_digits = 8;
+ pi->max_digits = 16;
pi->max_tries = 3;
pi->check_cb = try_unprotect_cb;
arg.ctrl = ctrl;
Modified: trunk/configure.ac
===================================================================
--- trunk/configure.ac 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/configure.ac 2009-03-05 19:19:37 UTC (rev 4944)
@@ -24,8 +24,8 @@
# Remember to change the version number immediately *after* a release.
# Set my_issvn to "yes" for non-released code. Remember to run an
# "svn up" and "autogen.sh" right before creating a distribution.
-m4_define([my_version], [2.0.11])
-m4_define([my_issvn], [no])
+m4_define([my_version], [2.0.12])
+m4_define([my_issvn], [yes])
m4_define([svn_revision], m4_esyscmd([printf "%d" $(svn info 2>/dev/null \
| sed -n '/^Revision:/ s/[^0-9]//gp'|head -1)]))
Modified: trunk/doc/gpg.texi
===================================================================
--- trunk/doc/gpg.texi 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/doc/gpg.texi 2009-03-05 19:19:37 UTC (rev 4944)
@@ -425,7 +425,7 @@
keyring. The fast version is currently just a synonym.
There are a few other options which control how this command works.
-Most notable here is the @option{--keyserver-options merge-only} option
+Most notable here is the @option{--import-options merge-only} option
which does not insert new keys but does only the merging of new
signatures, user-IDs and subkeys.
Modified: trunk/doc/help.txt
===================================================================
--- trunk/doc/help.txt 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/doc/help.txt 2009-03-05 19:19:37 UTC (rev 4944)
@@ -135,7 +135,13 @@
.gpg.keygen.size
-Enter the size of the key.
+Enter the size of the key.
+
+The suggested default is usually a good choice.
+
+If you want to use a large key size, for example 4096 bit, please
+think again whether it really makes sense for you. You may want
+to view the web page http://www.xkcd.com/538/ .
.
.gpg.keygen.size.huge.okay
Modified: trunk/scd/app-nks.c
===================================================================
--- trunk/scd/app-nks.c 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/scd/app-nks.c 2009-03-05 19:19:37 UTC (rev 4944)
@@ -17,6 +17,27 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+/* Notes:
+
+ - This is still work in progress. We are now targeting TCOS 3 cards
+ but try to keep compatibility to TCOS 2. Both are not fully
+ working as of now. TCOS 3 PIN management seems to work. Use GPA
+ from SVN trunk to test it.
+
+ - If required, we automagically switch between the NKS application
+ and the SigG application. This avoids to use the DINSIG
+ application which is somewhat limited, has no support for Secure
+ Messaging as required by TCOS 3 and has no way to change the PIN
+ or even set the NullPIN.
+
+ - We use the prefix NKS-DF01 for TCOS 2 cards and NKS-NKS3 for newer
+ cards. This is because the NKS application has moved to DF02 with
+ TCOS 3 and thus we better use a DF independent tag.
+
+ - We use only the global PINs for the NKS application.
+
+ */
+
#include <config.h>
#include <errno.h>
#include <stdio.h>
@@ -30,9 +51,15 @@
#include "iso7816.h"
#include "app-common.h"
#include "tlv.h"
+#include "apdu.h"
+static char const aid_nks[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
+static char const aid_sigg[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
+
+
static struct
{
+ int is_sigg; /* Valid for SigG application. */
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. */
@@ -40,20 +67,23 @@
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, 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 }
+ { 0, 0x4531, 0, 0, 0xC000, 1, 0 }, /* EF_PK.NKS.SIG */
+ { 1, 0x4531, 3, 0, 0x0000, 1, 1 }, /* EF_PK.CH.SIG */
+ { 0, 0xC000, 0, 101 }, /* EF_C.NKS.SIG */
+ { 1, 0xC000, 0, 101 }, /* EF_C.CH.SIG */
+ { 0, 0x4331, 0, 100 },
+ { 0, 0x4332, 0, 100 },
+ { 0, 0xB000, 0, 110 }, /* EF_PK.RCA.NKS */
+ { 0, 0x45B1, 0, 0, 0xC200, 0, 1 }, /* EF_PK.NKS.ENC */
+ { 0, 0xC200, 0, 101 }, /* EF_C.NKS.ENC */
+ { 0, 0x43B1, 0, 100 },
+ { 0, 0x43B2, 0, 100 },
+ { 0, 0x4571, 3, 0, 0xc500, 0, 0 }, /* EF_PK.NKS.AUT */
+ { 0, 0xC500, 3, 101 }, /* EF_C.NKS.AUT */
+ { 0, 0x45B2, 3, 0, 0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */
+ { 0, 0xC201, 3, 101 }, /* EF_C.NKS.ENC1024 */
+/* { 1, 0xB000, 3, ... */
+ { 0, 0 }
};
@@ -62,10 +92,14 @@
struct app_local_s {
int nks_version; /* NKS version. */
+ int sigg_active; /* True if switched to the SigG application. */
};
+
+static gpg_error_t switch_application (app_t app, int enable_sigg);
+
/* Release local data. */
static void
@@ -146,7 +180,135 @@
}
+/* TCOS responds to a verify with empty data (i.e. without the Lc
+ byte) with the status of the PIN. PWID is the PIN ID, If SIGG is
+ true, the application is switched into SigG mode.
+ Returns:
+ -1 = Error retrieving the data,
+ -2 = No such PIN,
+ -3 = PIN blocked,
+ -4 = NullPIN activ,
+ n >= 0 = Number of verification attempts left. */
+static int
+get_chv_status (app_t app, int sigg, int pwid)
+{
+ unsigned char *result = NULL;
+ size_t resultlen;
+ char command[4];
+ int rc;
+ if (switch_application (app, sigg))
+ return sigg? -2 : -1; /* No such PIN / General error. */
+
+ command[0] = 0x00;
+ command[1] = 0x20;
+ command[2] = 0x00;
+ command[3] = pwid;
+
+ if (apdu_send_direct (app->slot, command, 4, 0, &result, &resultlen))
+ rc = -1; /* Error. */
+ else if (resultlen < 2)
+ rc = -1; /* Error. */
+ else
+ {
+ unsigned int sw = ((result[resultlen-2] << 8) | result[resultlen-1]);
+
+ if (sw == 0x6a88)
+ rc = -2; /* No such PIN. */
+ else if (sw == 0x6983)
+ rc = -3; /* PIN is blocked. */
+ else if (sw == 0x6985)
+ rc = -4; /* NullPIN is activ. */
+ else if ((sw & 0xfff0) == 0x63C0)
+ rc = (sw & 0x000f); /* PIN has N tries left. */
+ else
+ rc = -1; /* Other error. */
+ }
+ xfree (result);
+
+ return rc;
+}
+
+
+/* Implement the GETATTR command. This is similar to the LEARN
+ command but returns just one value via the status interface. */
+static gpg_error_t
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ static struct {
+ const char *name;
+ int special;
+ } table[] = {
+ { "$AUTHKEYID", 1 },
+ { "NKS-VERSION", 2 },
+ { "CHV-STATUS", 3 },
+ { NULL, 0 }
+ };
+ gpg_error_t err = 0;
+ int idx;
+ char buffer[100];
+
+ err = switch_application (app, 0);
+ if (err)
+ return err;
+
+ for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
+ ;
+ if (!table[idx].name)
+ return gpg_error (GPG_ERR_INV_NAME);
+
+ switch (table[idx].special)
+ {
+ case 1: /* $AUTHKEYID */
+ {
+ /* NetKey 3.0 cards define this key for authentication.
+ FIXME: We don't have the readkey command, so this
+ information is pretty useless. */
+ char const tmp[] = "NKS-NKS3.4571";
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ }
+ break;
+
+ case 2: /* NKS-VERSION */
+ snprintf (buffer, sizeof buffer, "%d", app->app_local->nks_version);
+ send_status_info (ctrl, table[idx].name,
+ buffer, strlen (buffer), NULL, 0);
+ break;
+
+ case 3: /* CHV-STATUS */
+ {
+ /* Returns: PW1.CH PW2.CH PW1.CH.SIG PW2.CH.SIG That are the
+ two global passwords followed by the two SigG passwords.
+ For the values, see the function get_chv_status. */
+ int tmp[4];
+
+ /* We use a helper array so that we can control that there is
+ no superfluous application switch. Note that PW2.CH.SIG
+ really has the identifier 0x83 and not 0x82 as one would
+ expect. */
+ tmp[0] = get_chv_status (app, 0, 0x00);
+ tmp[1] = get_chv_status (app, 0, 0x01);
+ tmp[2] = get_chv_status (app, 1, 0x81);
+ tmp[3] = get_chv_status (app, 1, 0x83);
+ snprintf (buffer, sizeof buffer,
+ "%d %d %d %d", tmp[0], tmp[1], tmp[2], tmp[3]);
+ send_status_info (ctrl, table[idx].name,
+ buffer, strlen (buffer), NULL, 0);
+ }
+ break;
+
+
+ default:
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ break;
+ }
+
+ return err;
+}
+
+
+
+
static gpg_error_t
do_learn_status (app_t app, ctrl_t ctrl)
{
@@ -154,12 +316,19 @@
char ct_buf[100], id_buf[100];
int i;
- /* Output information about all useful objects. */
+ err = switch_application (app, 0);
+ if (err)
+ return err;
+
+ /* Output information about all useful objects in the NKS application. */
for (i=0; filelist[i].fid; i++)
{
if (filelist[i].nks_ver > app->app_local->nks_version)
continue;
+ if (filelist[i].is_sigg)
+ continue;
+
if (filelist[i].certtype)
{
size_t len;
@@ -171,8 +340,10 @@
/* FIXME: We should store the length in the application's
context so that a following readcert does only need to
read that many bytes. */
- sprintf (ct_buf, "%d", filelist[i].certtype);
- sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
+ snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
+ snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
+ app->app_local->nks_version < 3? "DF01":"NKS3",
+ filelist[i].fid);
send_status_info (ctrl, "CERTINFO",
ct_buf, strlen (ct_buf),
id_buf, strlen (id_buf),
@@ -189,7 +360,9 @@
filelist[i].fid, gpg_strerror (err));
else
{
- sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
+ snprintf (id_buf, sizeof id_buf, "NKS-%s.%04X",
+ app->app_local->nks_version < 3? "DF01":"NKS3",
+ filelist[i].fid);
send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40,
id_buf, strlen (id_buf),
@@ -198,6 +371,59 @@
}
}
+ err = switch_application (app, 1);
+ if (err)
+ return 0; /* Silently ignore if we can't swicth to SigG. */
+
+ for (i=0; filelist[i].fid; i++)
+ {
+ if (filelist[i].nks_ver > app->app_local->nks_version)
+ continue;
+
+ if (!filelist[i].is_sigg)
+ continue;
+
+ if (filelist[i].certtype)
+ {
+ size_t len;
+
+ len = app_help_read_length_of_cert (app->slot,
+ filelist[i].fid, NULL);
+ if (len)
+ {
+ /* FIXME: We should store the length in the application's
+ context so that a following readcert does only need to
+ read that many bytes. */
+ snprintf (ct_buf, sizeof ct_buf, "%d", filelist[i].certtype);
+ snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X",
+ filelist[i].fid);
+ send_status_info (ctrl, "CERTINFO",
+ ct_buf, strlen (ct_buf),
+ id_buf, strlen (id_buf),
+ NULL, (size_t)0);
+ }
+ }
+ else if (filelist[i].iskeypair)
+ {
+ char gripstr[40+1];
+
+ 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));
+ else
+ {
+ snprintf (id_buf, sizeof id_buf, "NKS-SIGG.%04X",
+ filelist[i].fid);
+ send_status_info (ctrl, "KEYPAIRINFO",
+ gripstr, 40,
+ id_buf, strlen (id_buf),
+ NULL, (size_t)0);
+ }
+ }
+ }
+
+
return 0;
}
@@ -223,7 +449,16 @@
*cert = NULL;
*certlen = 0;
- if (strncmp (certid, "NKS-DF01.", 9) )
+
+ err = switch_application (app, 0);
+ if (err)
+ return err;
+
+ if (!strncmp (certid, "NKS-NKS3.", 9))
+ ;
+ else if (!strncmp (certid, "NKS-DF01.", 9))
+ ;
+ else
return gpg_error (GPG_ERR_INV_ID);
certid += 9;
if (!hexdigitp (certid) || !hexdigitp (certid+1)
@@ -331,21 +566,34 @@
}
+static gpg_error_t
+basic_pin_checks (const char *pinvalue, int minlen, int maxlen)
+{
+ if (strlen (pinvalue) < minlen)
+ {
+ log_error ("PIN is too short; minimum length is %d\n", minlen);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+ if (strlen (pinvalue) > maxlen)
+ {
+ log_error ("PIN is too large; maximum length is %d\n", maxlen);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+ return 0;
+}
+
+
/* Verify the PIN if required. */
static gpg_error_t
-verify_pin (app_t app,
+verify_pin (app_t app, int pwid, const char *desc,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
iso7816_pininfo_t pininfo;
int rc;
- /* Note that force_chv1 is never set but we do it here anyway so
- that other applications may reuse this function. For example it
- makes sense to set force_chv1 for German signature law cards.
- NKS is very similar to the DINSIG draft standard. */
- if ( app->did_chv1 && !app->force_chv1 )
- return 0; /* No need to verify it again. */
+ if (!desc)
+ desc = "PIN";
memset (&pininfo, 0, sizeof pininfo);
pininfo.mode = 1;
@@ -375,7 +623,7 @@
{
char *pinvalue;
- rc = pincb (pincb_arg, "PIN", &pinvalue);
+ rc = pincb (pincb_arg, desc, &pinvalue);
if (rc)
{
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
@@ -384,24 +632,14 @@
/* The following limits are due to TCOS but also defined in the
NKS specs. */
- if (strlen (pinvalue) < pininfo.minlen)
+ rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen);
+ if (rc)
{
- log_error ("PIN is too short; minimum length is %d\n",
- pininfo.minlen);
xfree (pinvalue);
- return gpg_error (GPG_ERR_BAD_PIN);
+ return rc;
}
- else if (strlen (pinvalue) > pininfo.maxlen)
- {
- log_error ("PIN is too large; maximum length is %d\n",
- pininfo.maxlen);
- xfree (pinvalue);
- return gpg_error (GPG_ERR_BAD_PIN);
- }
- /* Although it is possible to use a local PIN, we use the global
- PIN for this application. */
- rc = iso7816_verify (app->slot, 0, pinvalue, strlen (pinvalue));
+ rc = iso7816_verify (app->slot, pwid, pinvalue, strlen (pinvalue));
xfree (pinvalue);
}
@@ -413,7 +651,6 @@
log_error ("verify PIN failed\n");
return rc;
}
- app->did_chv1 = 1;
return 0;
}
@@ -447,9 +684,17 @@
if (indatalen != 20 && indatalen != 16 && indatalen != 35)
return gpg_error (GPG_ERR_INV_VALUE);
+ rc = switch_application (app, 0);
+ if (rc)
+ return rc;
+
/* Check that the provided ID is valid. This is not really needed
but we do it to enforce correct usage by the caller. */
- if (strncmp (keyidstr, "NKS-DF01.", 9) )
+ if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
+ ;
+ else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
+ ;
+ else
return gpg_error (GPG_ERR_INV_ID);
keyidstr += 9;
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
@@ -490,7 +735,7 @@
memcpy (data+15, indata, indatalen);
}
- rc = verify_pin (app, pincb, pincb_arg);
+ rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
if (!rc)
rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
return rc;
@@ -519,9 +764,17 @@
if (!keyidstr || !*keyidstr || !indatalen)
return gpg_error (GPG_ERR_INV_VALUE);
+ rc = switch_application (app, 0);
+ if (rc)
+ return rc;
+
/* Check that the provided ID is valid. This is not really needed
but we do it to to enforce correct usage by the caller. */
- if (strncmp (keyidstr, "NKS-DF01.", 9) )
+ if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
+ ;
+ else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
+ ;
+ else
return gpg_error (GPG_ERR_INV_ID);
keyidstr += 9;
if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
@@ -542,7 +795,7 @@
0xC1, 0xB8,
mse_parm, sizeof mse_parm);
if (!rc)
- rc = verify_pin (app, pincb, pincb_arg);
+ rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
if (!rc)
rc = iso7816_decipher (app->slot, indata, indatalen, 0x81,
outdata, outdatalen);
@@ -550,67 +803,221 @@
}
-/* Handle the PASSWD command. CHVNOSTR is currently ignored; we
- always use VHV0. RESET_MODE is not yet implemented. */
+
+/* Parse a password ID string. Returns NULL on error or a string
+ suitable as passpahrse prompt on success. On success stores the
+ reference value for the password at R_PWID and a flag indicating
+ that the SigG application is to be used at R_SIGG. If NEW_MODE is
+ true, the returned description is suitable for a new Password.
+ Supported values for PWIDSTR are:
+
+ PW1.CH - Global password 1
+ PW2.CH - Global password 2
+ PW1.CH.SIG - SigG password 1
+ PW2.CH.SIG - SigG password 2
+ */
+static const char *
+parse_pwidstr (const char *pwidstr, int new_mode, int *r_sigg, int *r_pwid)
+{
+ const char *desc;
+
+ if (!pwidstr)
+ desc = NULL;
+ else if (!strcmp (pwidstr, "PW1.CH"))
+ {
+ *r_sigg = 0;
+ *r_pwid = 0x00;
+ /* TRANSLATORS: Do not translate the "|*|" prefixes but keep
+ them verbatim at the start of the string. */
+ desc = (new_mode
+ ? _("|N|Please enter a new PIN for the standard keys.")
+ : _("||Please enter the PIN for the standard keys."));
+ }
+ else if (!strcmp (pwidstr, "PW2.CH"))
+ {
+ *r_pwid = 0x01;
+ desc = (new_mode
+ ? _("|NP|Please enter a new PIN Unblocking Code (PUK) "
+ "for the standard keys.")
+ : _("|P|Please enter the PIN Unblocking Code (PUK) "
+ "for the standard keys."));
+ }
+ else if (!strcmp (pwidstr, "PW1.CH.SIG"))
+ {
+ *r_pwid = 0x81;
+ *r_sigg = 1;
+ desc = (new_mode
+ ? _("|N|Please enter a new PIN for the key to create "
+ "qualified signatures.")
+ : _("||Please enter the PIN for the key to create "
+ "qualified signatures."));
+ }
+ else if (!strcmp (pwidstr, "PW2.CH.SIG"))
+ {
+ *r_pwid = 0x83; /* Yes, that is 83 and not 82. */
+ *r_sigg = 1;
+ desc = (new_mode
+ ? _("|NP|Please enter a new PIN Unblocking Code (PUK) "
+ "for the key to create qualified signatures.")
+ : _("|P|Please enter the PIN Unblocking Code (PUK) "
+ "for the key to create qualified signatures."));
+ }
+ else
+ desc = NULL;
+
+ return desc;
+}
+
+
+/* Handle the PASSWD command. See parse_pwidstr() for allowed values
+ for CHVNOSTR. */
static gpg_error_t
-do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
+do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
gpg_error_t err;
- char *pinvalue;
- const char *oldpin;
+ char *newpin = NULL;
+ char *oldpin = NULL;
+ size_t newpinlen;
size_t oldpinlen;
+ int is_sigg;
+ const char *newdesc;
+ int pwid;
+ iso7816_pininfo_t pininfo;
(void)ctrl;
- (void)chvnostr;
- if ((flags & APP_CHANGE_FLAG_RESET))
- return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ /* The minimum length is enforced by TCOS, the maximum length is
+ just a reasonable value. */
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.minlen = 6;
+ pininfo.maxlen = 16;
+
+ newdesc = parse_pwidstr (pwidstr, 1, &is_sigg, &pwid);
+ if (!newdesc)
+ return gpg_error (GPG_ERR_INV_ID);
+ err = switch_application (app, is_sigg);
+ if (err)
+ return err;
+
if ((flags & APP_CHANGE_FLAG_NULLPIN))
{
- /* With the nullpin flag, we do not verify the PIN - it would fail
- if the Nullpin is still set. */
- oldpin = "\0\0\0\0\0";
+ /* With the nullpin flag, we do not verify the PIN - it would
+ fail if the Nullpin is still set. */
+ oldpin = xtrycalloc (1, 6);
+ if (!oldpin)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
oldpinlen = 6;
}
else
{
- err = verify_pin (app, pincb, pincb_arg);
+ const char *desc;
+ int dummy1, dummy2;
+
+ if ((flags & APP_CHANGE_FLAG_RESET))
+ {
+ /* Reset mode: Ask for the alternate PIN. */
+ const char *altpwidstr;
+
+ if (!strcmp (pwidstr, "PW1.CH"))
+ altpwidstr = "PW2.CH";
+ else if (!strcmp (pwidstr, "PW2.CH"))
+ altpwidstr = "PW1.CH";
+ else if (!strcmp (pwidstr, "PW1.CH.SIG"))
+ altpwidstr = "PW2.CH.SIG";
+ else if (!strcmp (pwidstr, "PW2.CH.SIG"))
+ altpwidstr = "PW1.CH.SIG";
+ else
+ {
+ err = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ desc = parse_pwidstr (altpwidstr, 0, &dummy1, &dummy2);
+ }
+ else
+ {
+ /* Regular change mode: Ask for the old PIN. */
+ desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2);
+ }
+ err = pincb (pincb_arg, desc, &oldpin);
if (err)
- return err;
- oldpin = NULL;
- oldpinlen = 0;
+ {
+ log_error ("error getting old PIN: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ oldpinlen = strlen (oldpin);
+ err = basic_pin_checks (oldpin, pininfo.minlen, pininfo.maxlen);
+ if (err)
+ goto leave;
}
- /* TRANSLATORS: Do not translate the "|*|" prefixes but
- keep it at the start of the string. We need this elsewhere
- to get some infos on the string. */
- err = pincb (pincb_arg, _("|N|New PIN"), &pinvalue);
+ err = pincb (pincb_arg, newdesc, &newpin);
if (err)
{
log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
- return err;
+ goto leave;
}
+ newpinlen = strlen (newpin);
+
+ err = basic_pin_checks (newpin, pininfo.minlen, pininfo.maxlen);
+ if (err)
+ goto leave;
- err = iso7816_change_reference_data (app->slot, 0x00,
- oldpin, oldpinlen,
- pinvalue, strlen (pinvalue));
- xfree (pinvalue);
+ if ((flags & APP_CHANGE_FLAG_RESET))
+ {
+ char *data;
+ size_t datalen = oldpinlen + newpinlen;
+
+ data = xtrymalloc (datalen);
+ if (!data)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (data, oldpin, oldpinlen);
+ memcpy (data+oldpinlen, newpin, newpinlen);
+ err = iso7816_reset_retry_counter_with_rc (app->slot, pwid,
+ data, datalen);
+ wipememory (data, datalen);
+ xfree (data);
+ }
+ else
+ err = iso7816_change_reference_data (app->slot, pwid,
+ oldpin, oldpinlen,
+ newpin, newpinlen);
+ leave:
+ xfree (oldpin);
+ xfree (newpin);
return err;
}
/* Perform a simple verify operation. KEYIDSTR should be NULL or empty. */
static gpg_error_t
-do_check_pin (app_t app, const char *keyidstr,
+do_check_pin (app_t app, const char *pwidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
- (void)keyidstr;
- return verify_pin (app, pincb, pincb_arg);
+ gpg_error_t err;
+ int pwid;
+ int is_sigg;
+ const char *desc;
+
+ desc = parse_pwidstr (pwidstr, 0, &is_sigg, &pwid);
+ if (!desc)
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = switch_application (app, is_sigg);
+ if (err)
+ return err;
+
+ return verify_pin (app, pwid, desc, pincb, pincb_arg);
}
@@ -647,15 +1054,42 @@
}
+/* If ENABLE_SIGG is true switch to the SigG application if not yet
+ active. If false switch to the NKS application if not yet active.
+ Returns 0 on success. */
+static gpg_error_t
+switch_application (app_t app, int enable_sigg)
+{
+ gpg_error_t err;
+
+ if ((app->app_local->sigg_active && enable_sigg)
+ || (!app->app_local->sigg_active && !enable_sigg) )
+ return 0; /* Already switched. */
+
+ log_info ("app-nks: switching to %s\n", enable_sigg? "SigG":"NKS");
+ if (enable_sigg)
+ err = iso7816_select_application (app->slot, aid_sigg, sizeof aid_sigg, 0);
+ else
+ err = iso7816_select_application (app->slot, aid_nks, sizeof aid_nks, 0);
+
+ if (!err)
+ app->app_local->sigg_active = enable_sigg;
+ else
+ log_error ("app-nks: error switching to %s: %s\n",
+ enable_sigg? "SigG":"NKS", gpg_strerror (err));
+
+ return err;
+}
+
+
/* Select the NKS application. */
gpg_error_t
app_select_nks (app_t app)
{
- static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
int slot = app->slot;
int rc;
- rc = iso7816_select_application (slot, aid, sizeof aid, 0);
+ rc = iso7816_select_application (slot, aid_nks, sizeof aid_nks, 0);
if (!rc)
{
app->apptype = "NKS";
@@ -674,7 +1108,7 @@
app->fnc.deinit = do_deinit;
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert;
- app->fnc.getattr = NULL;
+ app->fnc.getattr = do_getattr;
app->fnc.setattr = NULL;
app->fnc.genkey = NULL;
app->fnc.sign = do_sign;
Modified: trunk/scd/app-openpgp.c
===================================================================
--- trunk/scd/app-openpgp.c 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/scd/app-openpgp.c 2009-03-05 19:19:37 UTC (rev 4944)
@@ -1,5 +1,6 @@
/* app-openpgp.c - The OpenPGP card application.
- * Copyright (C) 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2005, 2007, 2008,
+ * 2009 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -1402,7 +1403,8 @@
int chvno, unsigned long sigcount, char **pinvalue)
{
int rc = 0;
- char *prompt;
+ char *prompt_buffer = NULL;
+ const char *prompt;
iso7816_pininfo_t pininfo;
int minlen = 6;
@@ -1432,30 +1434,34 @@
memset (&pininfo, 0, sizeof pininfo);
pininfo.mode = 1;
pininfo.minlen = minlen;
+
+
+ if (chvno == 1)
+ {
+#define PROMPTSTRING _("||Please enter the PIN%%0A[sigs done: %lu]")
+ size_t promptsize = strlen (PROMPTSTRING) + 50;
+
+ prompt_buffer = xtrymalloc (promptsize);
+ if (!prompt_buffer)
+ return gpg_error_from_syserror ();
+ snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, sigcount);
+ prompt = prompt_buffer;
+#undef PROMPTSTRING
+ }
+ else
+ prompt = _("||Please enter the PIN");
+
if (!opt.disable_keypad
&& !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
{
- /* The reader supports the verify command through the keypad. */
-
- if (chvno == 1)
- {
-#define PROMPTSTRING _("||Please enter your PIN at the reader's keypad%%0A" \
- "[sigs done: %lu]")
- size_t promptsize = strlen (PROMPTSTRING) + 50;
-
- prompt = xmalloc (promptsize);
- if (!prompt)
- return gpg_error_from_syserror ();
- snprintf (prompt, promptsize-1, PROMPTSTRING, sigcount);
- rc = pincb (pincb_arg, prompt, NULL);
- xfree (prompt);
-#undef PROMPTSTRING
- }
- else
- rc = pincb (pincb_arg,
- _("||Please enter your PIN at the reader's keypad"),
- NULL);
+ /* The reader supports the verify command through the keypad.
+ Note that the pincb appends a text to the prompt telling the
+ user to use the keypad. */
+ rc = pincb (pincb_arg, prompt, NULL);
+ prompt = NULL;
+ xfree (prompt_buffer);
+ prompt_buffer = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
@@ -1471,23 +1477,10 @@
else
{
/* The reader has no keypad or we don't want to use it. */
-
- if (chvno == 1)
- {
-#define PROMPTSTRING _("||Please enter the PIN%%0A[sigs done: %lu]")
- size_t promptsize = strlen (PROMPTSTRING) + 50;
-
- prompt = xtrymalloc (promptsize);
- if (!prompt)
- return gpg_error_from_syserror ();
- snprintf (prompt, promptsize-1, PROMPTSTRING, sigcount);
- rc = pincb (pincb_arg, prompt, pinvalue);
- xfree (prompt);
-#undef PROMPTSTRING
- }
- else
- rc = pincb (pincb_arg, _("||Please enter the PIN"), pinvalue);
-
+ rc = pincb (pincb_arg, prompt, pinvalue);
+ prompt = NULL;
+ xfree (prompt_buffer);
+ prompt_buffer = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
@@ -1586,6 +1579,8 @@
iso7816_pininfo_t pininfo;
int minlen = 8;
int remaining;
+ char *prompt_buffer = NULL;
+ const char *prompt;
memset (&pininfo, 0, sizeof pininfo);
pininfo.mode = 1;
@@ -1610,31 +1605,33 @@
log_info(_("%d Admin PIN attempts remaining before card"
" is permanently locked\n"), remaining);
- if (!opt.disable_keypad
- && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+ if (remaining < 3)
{
- /* The reader supports the verify command through the keypad. */
-
- if (remaining < 3)
- {
-#define PROMPTSTRING _("|A|Please enter the Admin PIN" \
- " at the reader's keypad%%0A" \
+ /* TRANSLATORS: Do not translate the "|A|" prefix but keep
+ it at the start of the string. Use %%0A to force a
+ lienfeed. */
+#define PROMPTSTRING _("|A|Please enter the Admin PIN%%0A" \
"[remaining attempts: %d]")
- size_t promptsize = strlen (PROMPTSTRING) + 50;
- char *prompt;
-
- prompt = xmalloc (promptsize);
- if (!prompt)
- return gpg_error_from_syserror ();
- snprintf (prompt, promptsize-1, PROMPTSTRING, remaining);
- rc = pincb (pincb_arg, prompt, NULL);
- xfree (prompt);
+ size_t promptsize = strlen (PROMPTSTRING) + 50;
+
+ prompt_buffer = xtrymalloc (promptsize);
+ if (!prompt_buffer)
+ return gpg_error_from_syserror ();
+ snprintf (prompt_buffer, promptsize-1, PROMPTSTRING, remaining);
+ prompt = prompt_buffer;
#undef PROMPTSTRING
- }
- else
- rc = pincb (pincb_arg, _("|A|Please enter the Admin PIN"
- " at the reader's keypad"), NULL);
+ }
+ else
+ prompt = _("|A|Please enter the Admin PIN");
+ if (!opt.disable_keypad
+ && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+ {
+ /* The reader supports the verify command through the keypad. */
+ rc = pincb (pincb_arg, prompt, NULL);
+ prompt = NULL;
+ xfree (prompt_buffer);
+ prompt_buffer = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
@@ -1649,10 +1646,10 @@
{
char *pinvalue;
- /* TRANSLATORS: Do not translate the "|A|" prefix but keep
- it at the start of the string. We need this elsewhere to
- get some infos on the string. */
- rc = pincb (pincb_arg, _("|A|Admin PIN"), &pinvalue);
+ rc = pincb (pincb_arg, prompt, &pinvalue);
+ prompt = NULL;
+ xfree (prompt_buffer);
+ prompt_buffer = NULL;
if (rc)
{
log_info (_("PIN callback returned error: %s\n"),
Modified: trunk/scd/command.c
===================================================================
--- trunk/scd/command.c 2009-03-03 12:26:06 UTC (rev 4943)
+++ trunk/scd/command.c 2009-03-05 19:19:37 UTC (rev 4944)
@@ -1370,9 +1370,10 @@
/* PASSWD [--reset] [--nullpin] <chvno>
- Change the PIN or reset the retry counter of the card holder
- verfication vector CHVNO. The option --nullpin is used for TCOS
- cards to set the initial PIN. */
+ Change the PIN or, if --reset is given, reset the retry counter of
+ the card holder verfication vector CHVNO. The option --nullpin is
+ used for TCOS cards to set the initial PIN. The format of CHVNO
+ depends on the card application. */
static int
cmd_passwd (assuan_context_t ctx, char *line)
{
@@ -1435,13 +1436,27 @@
literal string "[CHV3]": In this case the Admin PIN is checked
if and only if the retry counter is still at 3.
+ For Netkey:
+
+ Any of the valid PIN Ids may be used. These are the strings:
+
+ PW1.CH - Global password 1
+ PW2.CH - Global password 2
+ PW1.CH.SIG - SigG password 1
+ PW2.CH.SIG - SigG password 2
+
+ For a definitive list, see the implementation in app-nks.c.
+ Note that we call a PW2.* PIN a "PUK" despite that since TCOS
+ 3.0 they are technically alternative PINs used to mutally
+ unblock each other.
+
*/
static int
cmd_checkpin (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
- char *keyidstr;
+ char *idstr;
if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED);
@@ -1455,14 +1470,12 @@
/* We have to use a copy of the key ID because the function may use
the pin_cb which in turn uses the assuan line buffer and thus
overwriting the original line with the keyid. */
- keyidstr = xtrystrdup (line);
- if (!keyidstr)
+ idstr = xtrystrdup (line);
+ if (!idstr)
return out_of_core ();
- rc = app_check_pin (ctrl->app_ctx,
- keyidstr,
- pin_cb, ctx);
- xfree (keyidstr);
+ rc = app_check_pin (ctrl->app_ctx, idstr, pin_cb, ctx);
+ xfree (idstr);
if (rc)
log_error ("app_check_pin failed: %s\n", gpg_strerror (rc));
@@ -1566,7 +1579,7 @@
deny_admin - Returns OK if admin commands are not allowed or
GPG_ERR_GENERAL if admin commands are allowed.
- app_list - Return a list of supported applciations. One
+ app_list - Return a list of supported applications. One
application per line, fields delimited by colons,
first field is the name.
*/
More information about the Gnupg-commits
mailing list