New PIN Callback attributes in gpg-agent.

Common prompts for keypad and simple card reader.
More support for Netkey cards;  PIN management works now.
This commit is contained in:
Werner Koch 2009-03-05 19:19:37 +00:00
parent 2749c6bcd9
commit 59d7a54e72
13 changed files with 687 additions and 163 deletions

4
NEWS
View File

@ -1,3 +1,7 @@
Noteworthy changes in version 2.0.12
-------------------------------------------------
Noteworthy changes in version 2.0.11 (2009-03-03)
-------------------------------------------------

View File

@ -1,3 +1,9 @@
2009-03-05 Werner Koch <wk@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@g10code.com>
* command.c (cmd_getinfo): Add subcommand "scd_running".

View File

@ -1061,7 +1061,7 @@ cmd_learn (assuan_context_t ctx, char *line)
/* 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)
{

View File

@ -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 @@ encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
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 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
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 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
{
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 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
}
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 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
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 @@ getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
}
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);

View File

@ -367,7 +367,7 @@ unprotect (ctrl_t ctrl, const char *desc_text,
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;

View File

@ -24,8 +24,8 @@ min_automake_version="1.10"
# 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)]))

View File

@ -1,3 +1,12 @@
2009-03-04 Werner Koch <wk@g10code.com>
* help.txt (gpg.keygen.size): Add a link to web page.
2009-03-03 Werner Koch <wk@g10code.com>
* gpg.texi (Operational GPG Commands): "merge-only" is an
import-option. Reported by Joseph Oreste Bruni.
2009-03-02 Werner Koch <wk@g10code.com>
* gpg-agent.texi (Invoking GPG-AGENT): Modernized instructions.

View File

@ -425,7 +425,7 @@ Import/merge keys. This adds the given keys to the
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.

View File

@ -135,7 +135,13 @@ Please consult your security expert first.
.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

View File

@ -1,3 +1,30 @@
2009-03-05 Werner Koch <wk@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@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@g10code.com>
* command.c (scd_command_handler): Remove dereference of STOPME

View File

@ -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 @@ static struct
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,9 +92,13 @@ static struct
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. */
@ -146,6 +180,134 @@ keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr)
}
/* 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 @@ do_learn_status (app_t app, ctrl_t ctrl)
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 @@ do_learn_status (app_t app, ctrl_t ctrl)
/* 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 @@ do_learn_status (app_t app, ctrl_t ctrl)
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 @@ do_learn_status (app_t app, ctrl_t ctrl)
}
}
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 @@ do_readcert (app_t app, const char *certid,
*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 @@ do_readcert (app_t app, const char *certid,
}
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 @@ verify_pin (app_t app,
{
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 @@ verify_pin (app_t app,
/* 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);
}
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);
return rc;
}
/* 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 @@ verify_pin (app_t app,
log_error ("verify PIN failed\n");
return rc;
}
app->did_chv1 = 1;
return 0;
}
@ -447,9 +684,17 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
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 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
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 @@ do_decipher (app_t app, const char *keyidstr,
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 @@ do_decipher (app_t app, const char *keyidstr,
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 @@ do_decipher (app_t app, const char *keyidstr,
}
/* 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 @@ get_nks_version (int slot)
}
/* 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_select_nks (app_t app)
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;

View File

@ -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 @@ verify_a_chv (app_t app,
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 @@ verify_a_chv (app_t app,
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 @@ verify_a_chv (app_t app,
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 @@ verify_chv3 (app_t app,
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 @@ verify_chv3 (app_t app,
log_info(_("%d Admin PIN attempts remaining before card"
" is permanently locked\n"), remaining);
if (remaining < 3)
{
/* 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;
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
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. */
if (remaining < 3)
{
#define PROMPTSTRING _("|A|Please enter the Admin PIN" \
" at the reader's keypad%%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);
#undef PROMPTSTRING
}
else
rc = pincb (pincb_arg, _("|A|Please enter the Admin PIN"
" at the reader's keypad"), NULL);
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 @@ verify_chv3 (app_t app,
{
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"),

View File

@ -1370,9 +1370,10 @@ cmd_random (assuan_context_t ctx, char *line)
/* 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 @@ cmd_passwd (assuan_context_t ctx, char *line)
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 @@ cmd_checkpin (assuan_context_t ctx, char *line)
/* 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 @@ cmd_unlock (assuan_context_t ctx, char *line)
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.
*/