scd: Implement PIN changing and unblocking for PIV cards.

* scd/app-piv.c: Some refactoring
(do_change_chv): Implement.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-02-06 09:45:54 +01:00
parent 3231ecdafd
commit e9e876cb55
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
2 changed files with 278 additions and 99 deletions

View File

@ -20,6 +20,24 @@
/* Some notes: /* Some notes:
* - Specs for PIV are at http://dx.doi.org/10.6028/NIST.SP.800-73-4 * - Specs for PIV are at http://dx.doi.org/10.6028/NIST.SP.800-73-4
* *
* - Access control matrix:
* | Action | 9B | PIN | PUK | |
* |--------------+-----+-----+-----+------------------------------|
* | Generate key | yes | | | |
* | Change 9B | yes | | | |
* | Change retry | yes | yes | | Yubikey only |
* | Import key | yes | | | |
* | Import cert | yes | | | |
* | Change CHUID | yes | | | |
* | Reset card | | | | PIN and PUK in blocked state |
* | Verify PIN | | yes | | |
* | Sign data | | yes | | |
* | Decrypt data | | yes | | |
* | Change PIN | | yes | | |
* | Change PUK | | | yes | |
* | Unblock PIN | | | yes | New PIN required |
* |---------------------------------------------------------------|
* (9B indicates the 24 byte PIV Card Application Administration Key)
*/ */
#include <config.h> #include <config.h>
@ -389,10 +407,10 @@ dump_all_do (int slot)
/* Parse the key reference KEYREFSTR which is expected to hold a key /* 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 * reference for a CHV object. Return the one octet keyref or -1 for
* an invalid reference. */ * an invalid reference. */
static int static int
parse_pin_keyref (const char *keyrefstr) parse_chv_keyref (const char *keyrefstr)
{ {
if (!keyrefstr) if (!keyrefstr)
return -1; return -1;
@ -457,7 +475,7 @@ get_chv_status (app_t app, const char *keyrefstr)
int result; int result;
int keyref; int keyref;
keyref = parse_pin_keyref (keyrefstr); keyref = parse_chv_keyref (keyrefstr);
if (!keyrefstr) if (!keyrefstr)
return -1; return -1;
@ -467,7 +485,7 @@ get_chv_status (app_t app, const char *keyrefstr)
apdu[3] = keyref; 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))
result = -5; /* No need to verification. */ result = -5; /* No need to verification. */
else if (sw == 0x6a88) else if (sw == 0x6a88 || sw == 0x6a80)
result = -2; /* No such PIN. */ result = -2; /* No such PIN. */
else if (sw == 0x6983) else if (sw == 0x6983)
result = -3; /* PIN is blocked. */ result = -3; /* PIN is blocked. */
@ -540,7 +558,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
} }
else if (table[idx].special == -4) /* CHV-STATUS */ else if (table[idx].special == -4) /* CHV-STATUS */
{ {
int tmp[3]; int tmp[4];
tmp[0] = get_chv_status (app, "PIV.00"); tmp[0] = get_chv_status (app, "PIV.00");
tmp[1] = get_chv_status (app, "PIV.80"); tmp[1] = get_chv_status (app, "PIV.80");
@ -1177,40 +1195,29 @@ make_prompt (app_t app, int remaining, const char *firstline)
} }
/* Verify the Application PIN KEYREF. */ /* Helper for verify_chv to ask for the PIN and to prepare/pad it. On
* success the result is stored at (R_PIN,R_PINLEN). */
static gpg_error_t static gpg_error_t
verify_pin (app_t app, int keyref, ask_and_prepare_chv (app_t app, int keyref, int ask_new, int remaining,
gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg) gpg_error_t (*pincb)(void*,const char *,char **),
void *pincb_arg, char **r_pin, unsigned int *r_pinlen)
{ {
gpg_error_t err; gpg_error_t err;
unsigned char apdu[4];
unsigned int sw;
int remaining;
const char *label; const char *label;
char *prompt; char *prompt;
char *pinvalue = NULL; char *pinvalue = NULL;
unsigned int pinlen; unsigned int pinlen;
char pinbuffer[8]; char *pinbuffer = NULL;
int minlen, maxlen, padding, onlydigits; int minlen, maxlen, padding, onlydigits;
/* First check whether a verify is at all needed. This is done with *r_pin = NULL;
* P1 being 0 and no Lc and command data send. */ *r_pinlen = 0;
apdu[0] = 0x00;
apdu[1] = ISO7816_VERIFY; if (ask_new)
apdu[2] = 0x00;
apdu[3] = keyref;
if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
{
/* No need to verification. */
return 0; /* All fine. */
}
if ((sw & 0xfff0) == 0x63C0)
remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */
else
remaining = -1; remaining = -1;
if (remaining != -1) if (remaining != -1)
log_debug ("piv: PIN %2X has %d attempts left\n", keyref, remaining); log_debug ("piv: CHV %02X has %d attempts left\n", keyref, remaining);
switch (keyref) switch (keyref)
{ {
@ -1219,21 +1226,24 @@ verify_pin (app_t app, int keyref,
maxlen = 8; maxlen = 8;
padding = 1; padding = 1;
onlydigits = 1; onlydigits = 1;
label = _("||Please enter the Global-PIN of your PIV card"); label = (ask_new? _("|N|Please enter the new Global-PIN")
/**/ : _("||Please enter the Global-PIN of your PIV card"));
break; break;
case 0x80: case 0x80:
minlen = 6; minlen = 6;
maxlen = 8; maxlen = 8;
padding = 1; padding = 1;
onlydigits = 1; onlydigits = 1;
label = _("||Please enter the PIN of your PIV card"); label = (ask_new? _("|N|Please enter the new PIN")
/**/ : _("||Please enter the PIN of your PIV card"));
break; break;
case 0x81: case 0x81:
minlen = 8; minlen = 8;
maxlen = 8; maxlen = 8;
padding = 0; padding = 0;
onlydigits = 0; onlydigits = 0;
label = _("||Please enter the Unblocking Key of your PIV card"); label = (ask_new? _("|N|Please enter the new Unblocking Key")
/**/ :_("||Please enter the Unblocking Key of your PIV card"));
break; break;
case 0x96: case 0x96:
@ -1245,8 +1255,6 @@ verify_pin (app_t app, int keyref,
default: default:
return gpg_error (GPG_ERR_INV_ID); 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, label); prompt = make_prompt (app, remaining, label);
@ -1282,21 +1290,72 @@ verify_pin (app_t app, int keyref,
xfree (pinvalue); xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN); return gpg_error (GPG_ERR_BAD_PIN);
} }
pinbuffer = xtrymalloc_secure (maxlen);
if (!pinbuffer)
{
err = gpg_error_from_syserror ();
wipememory (pinvalue, pinlen);
xfree (pinvalue);
return err;
}
memcpy (pinbuffer, pinvalue, pinlen); memcpy (pinbuffer, pinvalue, pinlen);
wipememory (pinvalue, pinlen);
xfree (pinvalue);
if (padding) if (padding)
{ {
memset (pinbuffer + pinlen, 0xff, maxlen - pinlen); memset (pinbuffer + pinlen, 0xff, maxlen - pinlen);
wipememory (pinvalue, pinlen);
pinlen = maxlen; pinlen = maxlen;
} }
else
wipememory (pinvalue, pinlen);
xfree (pinvalue);
err = iso7816_verify (app->slot, keyref, pinbuffer, pinlen); *r_pin = pinbuffer;
wipememory (pinbuffer, pinlen); *r_pinlen = pinlen;
return 0;
}
/* Verify the card holder verification identified by KEYREF. This is
* either the Appication PIN or the Global PIN. */
static gpg_error_t
verify_chv (app_t app, int keyref,
gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg)
{
gpg_error_t err;
unsigned char apdu[4];
unsigned int sw;
int remaining;
char *pin = NULL;
unsigned int pinlen;
/* First check whether a verify is at all needed. This is done with
* P1 being 0 and no Lc and command data send. */
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))
{
/* No need to verification. */
return 0; /* All fine. */
}
if ((sw & 0xfff0) == 0x63C0)
remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */
else
remaining = -1;
err = ask_and_prepare_chv (app, keyref, 0, remaining, pincb, pincb_arg,
&pin, &pinlen);
if (err) if (err)
log_error ("PIN %02X verification failed: %s\n", keyref,gpg_strerror (err)); return err;
err = iso7816_verify (app->slot, keyref, pin, pinlen);
wipememory (pin, pinlen);
xfree (pin);
if (err)
log_error ("CHV %02X verification failed: %s\n",
keyref, gpg_strerror (err));
return err; return err;
} }
@ -1309,40 +1368,41 @@ verify_pin (app_t app, int keyref,
* PIV.81 - The PIN Unblocking key * PIV.81 - The PIN Unblocking key
* The supported flags are: * The supported flags are:
* APP_CHANGE_FLAG_CLEAR Clear the PIN verification state. * APP_CHANGE_FLAG_CLEAR Clear the PIN verification state.
* APP_CHANGE_FLAG_RESET Reset a PIN using the PUK. Only
* allowed with PIV.80.
*/ */
static gpg_error_t static gpg_error_t
do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
unsigned int flags, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
gpg_error_t err; gpg_error_t err;
int keyref; int keyref, targetkeyref;
unsigned char apdu[4]; unsigned char apdu[4];
unsigned int sw;
char *newpin = NULL; int remaining;
char *oldpin = NULL; char *oldpin = NULL;
/* size_t newpinlen; */ unsigned int oldpinlen;
/* size_t oldpinlen; */ char *newpin = NULL;
/* const char *newdesc; */ unsigned int newpinlen;
/* int pwid; */
pininfo_t pininfo;
(void)ctrl; (void)ctrl;
(void)pincb;
(void)pincb_arg;
/* The minimum and maximum lengths are enforced by PIV. */ /* Check for unknown flags. */
memset (&pininfo, 0, sizeof pininfo); if ((flags & ~(APP_CHANGE_FLAG_CLEAR|APP_CHANGE_FLAG_RESET)))
pininfo.minlen = 6; {
pininfo.maxlen = 8; err = gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
goto leave;
}
keyref = parse_pin_keyref (pwidstr); /* Parse the keyref. */
targetkeyref = keyref = parse_chv_keyref (pwidstr);
if (keyref == -1) if (keyref == -1)
return gpg_error (GPG_ERR_INV_ID); {
err = gpg_error (GPG_ERR_INV_ID);
if ((flags & ~APP_CHANGE_FLAG_CLEAR)) goto leave;
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); }
/* First see whether the special --clear mode has been requested. */ /* First see whether the special --clear mode has been requested. */
if ((flags & APP_CHANGE_FLAG_CLEAR)) if ((flags & APP_CHANGE_FLAG_CLEAR))
@ -1355,7 +1415,82 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
goto leave; goto leave;
} }
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* Prepare reset mode. */
if ((flags & APP_CHANGE_FLAG_RESET))
{
if (keyref == 0x81)
{
err = gpg_error (GPG_ERR_INV_ID); /* Can't reset the PUK. */
goto leave;
}
/* Set the keyref to the PUK and keep the TARGETKEYREF. */
keyref = 0x81;
}
/* Get the remaining tries count. This is done by using the check
* for verified state feature. */
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))
remaining = -1; /* Already verified, thus full number of tries. */
else if ((sw & 0xfff0) == 0x63C0)
remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */
else
remaining = -1;
/* Ask for the old pin or puk. */
err = ask_and_prepare_chv (app, keyref, 0, remaining, pincb, pincb_arg,
&oldpin, &oldpinlen);
if (err)
return err;
/* Verify the old pin so that we don't prompt for the new pin if the
* old is wrong. This is not possible for the PUK, though. */
if (keyref != 0x81)
{
err = iso7816_verify (app->slot, keyref, oldpin, oldpinlen);
if (err)
{
log_error ("CHV %02X verification failed: %s\n",
keyref, gpg_strerror (err));
goto leave;
}
}
/* Ask for the new pin. */
err = ask_and_prepare_chv (app, targetkeyref, 1, -1, pincb, pincb_arg,
&newpin, &newpinlen);
if (err)
return err;
if ((flags & APP_CHANGE_FLAG_RESET))
{
char *buf = xtrymalloc_secure (oldpinlen + newpinlen);
if (!buf)
{
err = gpg_error_from_syserror ();
goto leave;
}
memcpy (buf, oldpin, oldpinlen);
memcpy (buf+oldpinlen, newpin, newpinlen);
err = iso7816_reset_retry_counter_with_rc (app->slot, targetkeyref,
buf, oldpinlen+newpinlen);
xfree (buf);
if (err)
log_error ("resetting CHV %02X using CHV %02X failed: %s\n",
targetkeyref, keyref, gpg_strerror (err));
}
else
{
err = iso7816_change_reference_data (app->slot, keyref,
oldpin, oldpinlen,
newpin, newpinlen);
if (err)
log_error ("CHV %02X changing PIN failed: %s\n",
keyref, gpg_strerror (err));
}
leave: leave:
xfree (oldpin); xfree (oldpin);
@ -1365,19 +1500,19 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
/* Perform a simple verify operation for the PIN specified by PWIDSTR. /* Perform a simple verify operation for the PIN specified by PWIDSTR.
* For valid values see do_change_pin. */ * For valid values see do_change_chv. */
static gpg_error_t static gpg_error_t
do_check_pin (app_t app, const char *pwidstr, do_check_chv (app_t app, const char *pwidstr,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
int keyref; int keyref;
keyref = parse_pin_keyref (pwidstr); keyref = parse_chv_keyref (pwidstr);
if (keyref == -1) if (keyref == -1)
return gpg_error (GPG_ERR_INV_ID); return gpg_error (GPG_ERR_INV_ID);
return verify_pin (app, keyref, pincb, pincb_arg); return verify_chv (app, keyref, pincb, pincb_arg);
} }
@ -1492,7 +1627,7 @@ do_auth (app_t app, const char *keyidstr,
} }
/* Now verify the Application PIN. */ /* Now verify the Application PIN. */
err = verify_pin (app, 0x80, pincb, pincb_arg); err = verify_chv (app, 0x80, pincb, pincb_arg);
if (err) if (err)
return err; return err;
@ -1675,8 +1810,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_chv;
app->fnc.check_pin = do_check_pin; app->fnc.check_pin = do_check_chv;
leave: leave:

View File

@ -899,7 +899,7 @@ list_piv (card_info_t info, estream_t fp)
switch (info->chvinfo[i]) switch (info->chvinfo[i])
{ {
case -1: s = "[error]"; break; case -1: s = "[error]"; break;
case -2: s = "-"; break; /* No such PIN */ case -2: s = "-"; break; /* No such PIN or info not available. */
case -3: s = "[blocked]"; break; case -3: s = "[blocked]"; break;
case -5: s = "[verified]"; break; case -5: s = "[verified]"; break;
default: s = "[?]"; break; default: s = "[?]"; break;
@ -950,15 +950,21 @@ static gpg_error_t
cmd_verify (card_info_t info, char *argstr) cmd_verify (card_info_t info, char *argstr)
{ {
gpg_error_t err; gpg_error_t err;
const char *pinref;
if (!info) if (!info)
return print_help ("verify [chvid]", 0); return print_help ("verify [chvid]", 0);
if (info->apptype == APP_TYPE_OPENPGP) if (*argstr)
err = scd_checkpin (info->serialno); pinref = argstr;
else if (info->apptype == APP_TYPE_OPENPGP)
pinref = info->serialno;
else if (info->apptype == APP_TYPE_PIV)
pinref = "PIV.80";
else else
err = scd_checkpin (argstr); return gpg_error (GPG_ERR_MISSING_VALUE);
err = scd_checkpin (pinref);
if (err) if (err)
log_error ("verify failed: %s <%s>\n", log_error ("verify failed: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err)); gpg_strerror (err), gpg_strsource (err));
@ -1845,30 +1851,48 @@ cmd_generate (card_info_t info)
/* Sub-menu to change a PIN. The presented options may depend on the /* Sub-menu to change a PIN. The presented options may depend on the
* the ALLOW_ADMIN flag. */ * the ALLOW_ADMIN flag. */
static gpg_error_t static gpg_error_t
cmd_passwd (card_info_t info, int allow_admin) cmd_passwd (card_info_t info, int allow_admin, char *argstr)
{ {
gpg_error_t err; gpg_error_t err;
char *answer = NULL; char *answer = NULL;
const char *pinref;
if (!info) if (!info)
return print_help return print_help
("PASSWD\n\n" ("PASSWD [PINREF]\n\n"
"Menu to change or unblock the PINs. Note that the\n" "Menu to change or unblock the PINs. Note that the\n"
"presented menu options depend on the type of card\n" "presented menu options depend on the type of card\n"
"and whether the admin mode is enabled.", "and whether the admin mode is enabled. For OpenPGP\n"
"and PIV cards defaults for PINREF are available.",
0); 0);
/* Convenience message because we did this in gpg --card-edit too. */ if (opt.interactive || opt.verbose)
if (info->apptype == APP_TYPE_OPENPGP) log_info (_("%s card no. %s detected\n"),
log_info (_("OpenPGP card no. %s detected\n"), app_type_string (info->apptype),
info->dispserialno? info->dispserialno : info->serialno); info->dispserialno? info->dispserialno : info->serialno);
if (!allow_admin) if (!allow_admin || info->apptype != APP_TYPE_OPENPGP)
{ {
err = scd_change_pin ("OPENPGP.1", 0); if (*argstr)
pinref = argstr;
else if (info->apptype == APP_TYPE_OPENPGP)
pinref = "OPENPGP.1";
else if (info->apptype == APP_TYPE_PIV)
pinref = "PIV.80";
else
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
err = scd_change_pin (pinref, 0);
if (err) if (err)
goto leave; goto leave;
log_info ("PIN changed.\n");
if (info->apptype == APP_TYPE_PIV
&& !ascii_strcasecmp (pinref, "PIV.81"))
log_info ("PUK changed.\n");
else
log_info ("PIN changed.\n");
} }
else if (info->apptype == APP_TYPE_OPENPGP) else if (info->apptype == APP_TYPE_OPENPGP)
{ {
@ -1959,23 +1983,43 @@ cmd_unblock (card_info_t info)
"command can be used to set a new PIN.", "command can be used to set a new PIN.",
0); 0);
if (info->apptype == APP_TYPE_OPENPGP) if (opt.interactive || opt.verbose)
log_info (_("OpenPGP card no. %s detected\n"), log_info (_("%s card no. %s detected\n"),
app_type_string (info->apptype),
info->dispserialno? info->dispserialno : info->serialno); info->dispserialno? info->dispserialno : info->serialno);
if (info->apptype == APP_TYPE_OPENPGP && !info->is_v2) if (info->apptype == APP_TYPE_OPENPGP)
log_error (_("This command is only available for version 2 cards\n"));
else if (info->apptype == APP_TYPE_OPENPGP && !info->chvinfo[1])
log_error (_("Reset Code not or not anymore available\n"));
else if (info->apptype == APP_TYPE_OPENPGP)
{ {
err = scd_change_pin ("OPENPGP.2", 0); if (!info->is_v2)
{
log_error (_("This command is only available for version 2 cards\n"));
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
}
else if (!info->chvinfo[1])
{
log_error (_("Reset Code not or not anymore available\n"));
err = gpg_error (GPG_ERR_PIN_BLOCKED);
}
else
{
err = scd_change_pin ("OPENPGP.2", 0);
if (!err)
log_info ("PIN changed.\n");
}
}
else if (info->apptype == APP_TYPE_PIV)
{
/* Unblock the Application PIN. */
err = scd_change_pin ("PIV.80", 1);
if (!err) if (!err)
log_info ("PIN changed.\n"); log_info ("PIN unblocked and changed.\n");
} }
else else
log_info ("Unblocking not yet supported for '%s'\n", {
app_type_string (info->apptype)); log_info ("Unblocking not yet supported for '%s'\n",
app_type_string (info->apptype));
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
return err; return err;
} }
@ -2079,15 +2123,15 @@ cmd_factoryreset (card_info_t info)
goto leave; goto leave;
} }
if (opt.interactive || opt.verbose)
log_info (_("%s card no. %s detected\n"),
app_type_string (info->apptype),
info->dispserialno? info->dispserialno : info->serialno);
if (!termstate || is_yubikey) if (!termstate || is_yubikey)
{ {
if (is_yubikey) if (!is_yubikey)
log_info (_("Yubikey no. %s with PIV application detected\n"),
info->dispserialno? info->dispserialno : info->serialno);
else
{ {
log_info (_("OpenPGP card no. %s detected\n"),
info->dispserialno? info->dispserialno : info->serialno);
if (!(info->status_indicator == 3 || info->status_indicator == 5)) if (!(info->status_indicator == 3 || info->status_indicator == 5))
{ {
/* Note: We won't see status-indicator 3 here because it /* Note: We won't see status-indicator 3 here because it
@ -2865,7 +2909,7 @@ dispatch_command (card_info_t info, const char *orig_command)
case cmdREADCERT: err = cmd_readcert (info, argstr); break; case cmdREADCERT: err = cmd_readcert (info, argstr); break;
case cmdFORCESIG: err = cmd_forcesig (info); break; case cmdFORCESIG: err = cmd_forcesig (info); break;
case cmdGENERATE: err = cmd_generate (info); break; case cmdGENERATE: err = cmd_generate (info); break;
case cmdPASSWD: err = cmd_passwd (info, 1); break; case cmdPASSWD: err = cmd_passwd (info, 1, argstr); break;
case cmdUNBLOCK: err = cmd_unblock (info); break; case cmdUNBLOCK: err = cmd_unblock (info); break;
case cmdFACTORYRESET: err = cmd_factoryreset (info); break; case cmdFACTORYRESET: err = cmd_factoryreset (info); break;
case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break; case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
@ -3131,7 +3175,7 @@ interactive_loop (void)
case cmdREADCERT: err = cmd_readcert (info, argstr); break; case cmdREADCERT: err = cmd_readcert (info, argstr); break;
case cmdFORCESIG: err = cmd_forcesig (info); break; case cmdFORCESIG: err = cmd_forcesig (info); break;
case cmdGENERATE: err = cmd_generate (info); break; case cmdGENERATE: err = cmd_generate (info); break;
case cmdPASSWD: err = cmd_passwd (info, allow_admin); break; case cmdPASSWD: err = cmd_passwd (info, allow_admin, argstr); break;
case cmdUNBLOCK: err = cmd_unblock (info); break; case cmdUNBLOCK: err = cmd_unblock (info); break;
case cmdFACTORYRESET: case cmdFACTORYRESET:
err = cmd_factoryreset (info); err = cmd_factoryreset (info);