mirror of
git://git.gnupg.org/gnupg.git
synced 2025-03-28 22:49:59 +01:00
scd: Support CHV-STATUS and CHECKPIN for PIV.
* scd/app-piv.c (parse_pin_keyref): New. (get_chv_status): New. (do_getattr): Add name CHV-STATUS. (verify_pin): Add arg keyref to support other PINs. (do_change_pin): New. Right now limited to --clear. (do_check_pin): New. (app_select_piv): Register new commands. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
29929e6552
commit
fa9d703de5
237
scd/app-piv.c
237
scd/app-piv.c
@ -364,6 +364,25 @@ dump_all_do (int slot)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse the key reference KEYREFSTR which is expected to hold a key
|
||||||
|
* reference for a PIN object. Return the one octet keyref or -1 for
|
||||||
|
* an invalid reference. */
|
||||||
|
static int
|
||||||
|
parse_pin_keyref (const char *keyrefstr)
|
||||||
|
{
|
||||||
|
if (!keyrefstr)
|
||||||
|
return -1;
|
||||||
|
else if (!ascii_strcasecmp (keyrefstr, "PIV.00"))
|
||||||
|
return 0x00;
|
||||||
|
else if (!ascii_strcasecmp (keyrefstr, "PIV.80"))
|
||||||
|
return 0x80;
|
||||||
|
else if (!ascii_strcasecmp (keyrefstr, "PIV.81"))
|
||||||
|
return 0x81;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Return an allocated string with the serial number in a format to be
|
/* Return an allocated string with the serial number in a format to be
|
||||||
* show to the user. With FAILMODE is true return NULL if such an
|
* show to the user. With FAILMODE is true return NULL if such an
|
||||||
* abbreviated S/N is not available, else return the full serial
|
* abbreviated S/N is not available, else return the full serial
|
||||||
@ -396,6 +415,47 @@ get_dispserialno (app_t app, int failmode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The verify command can be used to retrieve the security status of
|
||||||
|
* the card. Given the PIN name (e.g. "PIV.80" for thge application
|
||||||
|
* pin, a status is returned:
|
||||||
|
*
|
||||||
|
* -1 = Error retrieving the data,
|
||||||
|
* -2 = No such PIN,
|
||||||
|
* -3 = PIN blocked,
|
||||||
|
* -5 = Verify still valid,
|
||||||
|
* n >= 0 = Number of verification attempts left.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
get_chv_status (app_t app, const char *keyrefstr)
|
||||||
|
{
|
||||||
|
unsigned char apdu[4];
|
||||||
|
unsigned int sw;
|
||||||
|
int result;
|
||||||
|
int keyref;
|
||||||
|
|
||||||
|
keyref = parse_pin_keyref (keyrefstr);
|
||||||
|
if (!keyrefstr)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
apdu[0] = 0x00;
|
||||||
|
apdu[1] = ISO7816_VERIFY;
|
||||||
|
apdu[2] = 0x00;
|
||||||
|
apdu[3] = keyref;
|
||||||
|
if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
|
||||||
|
result = -5; /* No need to verification. */
|
||||||
|
else if (sw == 0x6a88)
|
||||||
|
result = -2; /* No such PIN. */
|
||||||
|
else if (sw == 0x6983)
|
||||||
|
result = -3; /* PIN is blocked. */
|
||||||
|
else if ((sw & 0xfff0) == 0x63C0)
|
||||||
|
result = (sw & 0x000f);
|
||||||
|
else
|
||||||
|
result = -1; /* Error. */
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Implementation of the GETATTR command. This is similar to the
|
/* Implementation of the GETATTR command. This is similar to the
|
||||||
* LEARN command but returns only one value via status lines. */
|
* LEARN command but returns only one value via status lines. */
|
||||||
static gpg_error_t
|
static gpg_error_t
|
||||||
@ -408,7 +468,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
|
|||||||
} table[] = {
|
} table[] = {
|
||||||
{ "SERIALNO", 0x0000, -1 },
|
{ "SERIALNO", 0x0000, -1 },
|
||||||
{ "$AUTHKEYID", 0x0000, -2 }, /* Default key for ssh. */
|
{ "$AUTHKEYID", 0x0000, -2 }, /* Default key for ssh. */
|
||||||
{ "$DISPSERIALNO",0x0000, -3 }
|
{ "$DISPSERIALNO",0x0000, -3 },
|
||||||
|
{ "CHV-STATUS", 0x0000, -4 }
|
||||||
};
|
};
|
||||||
gpg_error_t err = 0;
|
gpg_error_t err = 0;
|
||||||
int idx;
|
int idx;
|
||||||
@ -450,6 +511,16 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
|
|||||||
else
|
else
|
||||||
err = gpg_error (GPG_ERR_INV_NAME); /* No Abbreviated S/N. */
|
err = gpg_error (GPG_ERR_INV_NAME); /* No Abbreviated S/N. */
|
||||||
}
|
}
|
||||||
|
else if (table[idx].special == -4) /* CHV-STATUS */
|
||||||
|
{
|
||||||
|
int tmp[3];
|
||||||
|
|
||||||
|
tmp[0] = get_chv_status (app, "PIV.00");
|
||||||
|
tmp[1] = get_chv_status (app, "PIV.80");
|
||||||
|
tmp[2] = get_chv_status (app, "PIV.81");
|
||||||
|
err = send_status_printf (ctrl, table[idx].name, "%d %d %d",
|
||||||
|
tmp[0], tmp[1], tmp[2]);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err);
|
relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err);
|
||||||
@ -849,27 +920,28 @@ make_prompt (app_t app, int remaining, const char *firstline)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Verify the Application PIN KEYREF. */
|
||||||
/* Verify the Application PIN for use with data object DOBJ. */
|
|
||||||
static gpg_error_t
|
static gpg_error_t
|
||||||
verify_pin (app_t app, data_object_t dobj,
|
verify_pin (app_t app, int keyref,
|
||||||
gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg)
|
gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg)
|
||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
unsigned char apdu[4];
|
unsigned char apdu[4];
|
||||||
unsigned int sw;
|
unsigned int sw;
|
||||||
int remaining;
|
int remaining;
|
||||||
|
const char *label;
|
||||||
char *prompt;
|
char *prompt;
|
||||||
char *pinvalue = NULL;
|
char *pinvalue = NULL;
|
||||||
unsigned int pinlen;
|
unsigned int pinlen;
|
||||||
char pinbuffer[8];
|
char pinbuffer[8];
|
||||||
|
int minlen, maxlen, padding, onlydigits;
|
||||||
|
|
||||||
/* First check whether a verify is at all needed. This is done with
|
/* First check whether a verify is at all needed. This is done with
|
||||||
* P1 being 0 and no Lc and command data send. */
|
* P1 being 0 and no Lc and command data send. */
|
||||||
apdu[0] = 0x00;
|
apdu[0] = 0x00;
|
||||||
apdu[1] = ISO7816_VERIFY;
|
apdu[1] = ISO7816_VERIFY;
|
||||||
apdu[2] = 0x00;
|
apdu[2] = 0x00;
|
||||||
apdu[3] = 0x80;
|
apdu[3] = keyref;
|
||||||
if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
|
if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
|
||||||
{
|
{
|
||||||
/* No need to verification. */
|
/* No need to verification. */
|
||||||
@ -881,11 +953,46 @@ verify_pin (app_t app, data_object_t dobj,
|
|||||||
remaining = -1;
|
remaining = -1;
|
||||||
|
|
||||||
if (remaining != -1)
|
if (remaining != -1)
|
||||||
log_debug ("piv: PIN for %s has %d attempts left\n",
|
log_debug ("piv: PIN %2X has %d attempts left\n", keyref, remaining);
|
||||||
dobj->keyref, remaining);
|
|
||||||
|
switch (keyref)
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
minlen = 6;
|
||||||
|
maxlen = 8;
|
||||||
|
padding = 1;
|
||||||
|
onlydigits = 1;
|
||||||
|
label = _("||Please enter the Global-PIN of your PIV card");
|
||||||
|
break;
|
||||||
|
case 0x80:
|
||||||
|
minlen = 6;
|
||||||
|
maxlen = 8;
|
||||||
|
padding = 1;
|
||||||
|
onlydigits = 1;
|
||||||
|
label = _("||Please enter the PIN of your PIV card");
|
||||||
|
break;
|
||||||
|
case 0x81:
|
||||||
|
minlen = 8;
|
||||||
|
maxlen = 8;
|
||||||
|
padding = 0;
|
||||||
|
onlydigits = 0;
|
||||||
|
label = _("||Please enter the Unblocking Key of your PIV card");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x96:
|
||||||
|
case 0x97:
|
||||||
|
case 0x98:
|
||||||
|
case 0x9B:
|
||||||
|
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return gpg_error (GPG_ERR_INV_ID);
|
||||||
|
}
|
||||||
|
log_assert (sizeof pinbuffer >= maxlen);
|
||||||
|
|
||||||
|
|
||||||
/* Ask for the PIN. */
|
/* Ask for the PIN. */
|
||||||
prompt = make_prompt (app, remaining, _("||Please enter your PIV PIN"));
|
prompt = make_prompt (app, remaining, label);
|
||||||
err = pincb (pincb_arg, prompt, &pinvalue);
|
err = pincb (pincb_arg, prompt, &pinvalue);
|
||||||
xfree (prompt);
|
xfree (prompt);
|
||||||
prompt = NULL;
|
prompt = NULL;
|
||||||
@ -896,24 +1003,22 @@ verify_pin (app_t app, data_object_t dobj,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pinlen = pinvalue? strlen (pinvalue) : 0;
|
pinlen = pinvalue? strlen (pinvalue) : 0;
|
||||||
if (pinlen < 6)
|
if (pinlen < minlen)
|
||||||
{
|
{
|
||||||
log_error (_("PIN for is too short;"
|
log_error (_("PIN for is too short; minimum length is %d\n"), minlen);
|
||||||
" minimum length is %d\n"), 6);
|
|
||||||
if (pinvalue)
|
if (pinvalue)
|
||||||
wipememory (pinvalue, pinlen);
|
wipememory (pinvalue, pinlen);
|
||||||
xfree (pinvalue);
|
xfree (pinvalue);
|
||||||
return gpg_error (GPG_ERR_BAD_PIN);
|
return gpg_error (GPG_ERR_BAD_PIN);
|
||||||
}
|
}
|
||||||
if (pinlen > sizeof pinbuffer)
|
if (pinlen > maxlen)
|
||||||
{
|
{
|
||||||
log_error (_("PIN for is too long;"
|
log_error (_("PIN for is too long; maximum length is %d\n"), maxlen);
|
||||||
" maximum length is %d\n"), (int)sizeof pinbuffer);
|
|
||||||
wipememory (pinvalue, pinlen);
|
wipememory (pinvalue, pinlen);
|
||||||
xfree (pinvalue);
|
xfree (pinvalue);
|
||||||
return gpg_error (GPG_ERR_BAD_PIN);
|
return gpg_error (GPG_ERR_BAD_PIN);
|
||||||
}
|
}
|
||||||
if (strspn (pinvalue, "0123456789") != pinlen)
|
if (onlydigits && strspn (pinvalue, "0123456789") != pinlen)
|
||||||
{
|
{
|
||||||
log_error (_("PIN has invalid characters; only digits are allowed\n"));
|
log_error (_("PIN has invalid characters; only digits are allowed\n"));
|
||||||
wipememory (pinvalue, pinlen);
|
wipememory (pinvalue, pinlen);
|
||||||
@ -921,20 +1026,102 @@ verify_pin (app_t app, data_object_t dobj,
|
|||||||
return gpg_error (GPG_ERR_BAD_PIN);
|
return gpg_error (GPG_ERR_BAD_PIN);
|
||||||
}
|
}
|
||||||
memcpy (pinbuffer, pinvalue, pinlen);
|
memcpy (pinbuffer, pinvalue, pinlen);
|
||||||
memset (pinbuffer + pinlen, 0xff, sizeof(pinbuffer) - pinlen);
|
if (padding)
|
||||||
wipememory (pinvalue, pinlen);
|
{
|
||||||
|
memset (pinbuffer + pinlen, 0xff, maxlen - pinlen);
|
||||||
|
wipememory (pinvalue, pinlen);
|
||||||
|
pinlen = maxlen;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
wipememory (pinvalue, pinlen);
|
||||||
xfree (pinvalue);
|
xfree (pinvalue);
|
||||||
|
|
||||||
err = iso7816_verify (app->slot, 0x80,
|
err = iso7816_verify (app->slot, keyref, pinbuffer, pinlen);
|
||||||
pinbuffer, sizeof pinbuffer);
|
wipememory (pinbuffer, pinlen);
|
||||||
wipememory (pinbuffer, sizeof pinbuffer);
|
|
||||||
if (err)
|
if (err)
|
||||||
log_error ("PIN verification failed: %s\n", gpg_strerror (err));
|
log_error ("PIN %02X verification failed: %s\n", keyref,gpg_strerror (err));
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Handle the PASSWD command. Valid values for PWIDSTR are
|
||||||
|
* key references related to PINs; in particular:
|
||||||
|
* PIV.00 - The Global PIN
|
||||||
|
* PIV.80 - The Application PIN
|
||||||
|
* PIV.81 - The PIN Unblocking key
|
||||||
|
* The supported flags are:
|
||||||
|
* APP_CHANGE_FLAG_CLEAR Clear the PIN verification state.
|
||||||
|
*/
|
||||||
|
static gpg_error_t
|
||||||
|
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;
|
||||||
|
int keyref;
|
||||||
|
unsigned char apdu[4];
|
||||||
|
|
||||||
|
char *newpin = NULL;
|
||||||
|
char *oldpin = NULL;
|
||||||
|
size_t newpinlen;
|
||||||
|
size_t oldpinlen;
|
||||||
|
const char *newdesc;
|
||||||
|
int pwid;
|
||||||
|
pininfo_t pininfo;
|
||||||
|
|
||||||
|
(void)ctrl;
|
||||||
|
|
||||||
|
/* The minimum and maximum lengths are enforced by PIV. */
|
||||||
|
memset (&pininfo, 0, sizeof pininfo);
|
||||||
|
pininfo.minlen = 6;
|
||||||
|
pininfo.maxlen = 8;
|
||||||
|
|
||||||
|
keyref = parse_pin_keyref (pwidstr);
|
||||||
|
if (keyref == -1)
|
||||||
|
return gpg_error (GPG_ERR_INV_ID);
|
||||||
|
|
||||||
|
if ((flags & ~APP_CHANGE_FLAG_CLEAR))
|
||||||
|
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||||
|
|
||||||
|
/* First see whether the special --clear mode has been requested. */
|
||||||
|
if ((flags & APP_CHANGE_FLAG_CLEAR))
|
||||||
|
{
|
||||||
|
apdu[0] = 0x00;
|
||||||
|
apdu[1] = ISO7816_VERIFY;
|
||||||
|
apdu[2] = 0xff;
|
||||||
|
apdu[3] = keyref;
|
||||||
|
err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
||||||
|
|
||||||
|
leave:
|
||||||
|
xfree (oldpin);
|
||||||
|
xfree (newpin);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Perform a simple verify operation for the PIN specified by PWIDSTR.
|
||||||
|
* For valid values see do_change_pin. */
|
||||||
|
static gpg_error_t
|
||||||
|
do_check_pin (app_t app, const char *pwidstr,
|
||||||
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
||||||
|
void *pincb_arg)
|
||||||
|
{
|
||||||
|
int keyref;
|
||||||
|
|
||||||
|
keyref = parse_pin_keyref (pwidstr);
|
||||||
|
if (keyref == -1)
|
||||||
|
return gpg_error (GPG_ERR_INV_ID);
|
||||||
|
|
||||||
|
return verify_pin (app, keyref, pincb, pincb_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Compute a digital signature using the GENERAL AUTHENTICATE command
|
/* Compute a digital signature using the GENERAL AUTHENTICATE command
|
||||||
* on INDATA which is expected to be the raw message digest. The
|
* on INDATA which is expected to be the raw message digest. The
|
||||||
* KEYIDSTR has the key reference or its OID (e.g. "PIV.9A"). The
|
* KEYIDSTR has the key reference or its OID (e.g. "PIV.9A"). The
|
||||||
@ -1045,8 +1232,8 @@ do_auth (app_t app, const char *keyidstr,
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now verify the PIN. */
|
/* Now verify the Application PIN. */
|
||||||
err = verify_pin (app, dobj, pincb, pincb_arg);
|
err = verify_pin (app, 0x80, pincb, pincb_arg);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@ -1226,8 +1413,8 @@ app_select_piv (app_t app)
|
|||||||
/* app->fnc.sign = do_sign; */
|
/* app->fnc.sign = do_sign; */
|
||||||
app->fnc.auth = do_auth;
|
app->fnc.auth = do_auth;
|
||||||
/* app->fnc.decipher = do_decipher; */
|
/* app->fnc.decipher = do_decipher; */
|
||||||
/* app->fnc.change_pin = do_change_pin; */
|
app->fnc.change_pin = do_change_pin;
|
||||||
/* app->fnc.check_pin = do_check_pin; */
|
app->fnc.check_pin = do_check_pin;
|
||||||
|
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user