1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

scd: Improve the prompts for OpenPGP cards.

* scd/app-openpgp.c (get_disp_name): New.
(get_disp_serialno): New.
(get_prompt_info): New.
(build_enter_admin_pin_prompt): Rework the prompt texts.  Factor some
code out to ...
(get_remaining_tries): New.
(verify_a_chv): Print a remaining counter also for the standard PIN.
Rework the prompt texts.

* agent/divert-scd.c (ask_for_card): Pretty format an OpenPGP serial
no.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2017-02-22 13:03:52 +01:00
parent 6488ffb767
commit e3944f34e3
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
2 changed files with 220 additions and 53 deletions

View File

@ -39,22 +39,39 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
char *serialno; char *serialno;
int no_card = 0; int no_card = 0;
char *desc; char *desc;
char *want_sn, *want_kid; char *want_sn, *want_kid, *want_sn_disp;
int want_sn_displen; int len;
*r_kid = NULL; *r_kid = NULL;
rc = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL); rc = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL);
if (rc) if (rc)
return rc; return rc;
want_sn_disp = xtrystrdup (want_sn);
if (!want_sn_disp)
{
rc = gpg_error_from_syserror ();
xfree (want_sn);
return rc;
}
/* We assume that a 20 byte serial number is a standard one which len = strlen (want_sn_disp);
has the property to have a zero in the last nibble (Due to BCD if (len == 32 && !strncmp (want_sn_disp, "D27600012401", 12))
representation). We don't display this '0' because it may {
confuse the user. */ /* This is an OpenPGP card - reformat */
want_sn_displen = strlen (want_sn); memmove (want_sn_disp, want_sn_disp+16, 4);
if (want_sn_displen == 20 && want_sn[19] == '0') want_sn_disp[4] = ' ';
want_sn_displen--; memmove (want_sn_disp+5, want_sn_disp+20, 8);
want_sn_disp[13] = 0;
}
else if (len == 20 && want_sn_disp[19] == '0')
{
/* We assume that a 20 byte serial number is a standard one
* which has the property to have a zero in the last nibble (Due
* to BCD representation). We don't display this '0' because it
* may confuse the user. */
want_sn_disp[19] = 0;
}
for (;;) for (;;)
{ {
@ -93,12 +110,12 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
{ {
if (asprintf (&desc, if (asprintf (&desc,
"%s:%%0A%%0A" "%s:%%0A%%0A"
" \"%.*s\"", " %s",
no_card no_card
? L_("Please insert the card with serial number") ? L_("Please insert the card with serial number")
: L_("Please remove the current card and " : L_("Please remove the current card and "
"insert the one with serial number"), "insert the one with serial number"),
want_sn_displen, want_sn) < 0) want_sn_disp) < 0)
{ {
rc = out_of_core (); rc = out_of_core ();
} }
@ -114,6 +131,7 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid)
} }
if (rc) if (rc)
{ {
xfree (want_sn_disp);
xfree (want_sn); xfree (want_sn);
xfree (want_kid); xfree (want_kid);
return rc; return rc;
@ -312,7 +330,8 @@ getpin_cb (void *opaque, const char *desc_text, const char *info,
info, NULL); info, NULL);
else else
desc2 = NULL; desc2 = NULL;
rc = agent_askpin (ctrl, desc2, prompt, again_text, pi, NULL, 0); rc = agent_askpin (ctrl, desc2? desc2 : info,
prompt, again_text, pi, NULL, 0);
xfree (desc2); xfree (desc2);
} }
again_text = NULL; again_text = NULL;
@ -401,6 +420,8 @@ divert_pksign (ctrl_t ctrl, const char *desc_text,
size_t siglen; size_t siglen;
unsigned char *sigval = NULL; unsigned char *sigval = NULL;
(void)desc_text;
rc = ask_for_card (ctrl, shadow_info, &kid); rc = ask_for_card (ctrl, shadow_info, &kid);
if (rc) if (rc)
return rc; return rc;
@ -409,7 +430,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text,
{ {
int save = ctrl->use_auth_call; int save = ctrl->use_auth_call;
ctrl->use_auth_call = 1; ctrl->use_auth_call = 1;
rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, desc_text, rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, NULL,
algo, digest, digestlen, &sigval, &siglen); algo, digest, digestlen, &sigval, &siglen);
ctrl->use_auth_call = save; ctrl->use_auth_call = save;
} }
@ -421,7 +442,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text,
rc = encode_md_for_card (digest, digestlen, algo, &data, &ndata); rc = encode_md_for_card (digest, digestlen, algo, &data, &ndata);
if (!rc) if (!rc)
{ {
rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, desc_text, rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, NULL,
algo, data, ndata, &sigval, &siglen); algo, data, ndata, &sigval, &siglen);
xfree (data); xfree (data);
} }
@ -458,6 +479,8 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
char *plaintext; char *plaintext;
size_t plaintextlen; size_t plaintextlen;
(void)desc_text;
*r_padding = -1; *r_padding = -1;
s = cipher; s = cipher;
@ -523,7 +546,7 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text,
if (rc) if (rc)
return rc; return rc;
rc = agent_card_pkdecrypt (ctrl, kid, getpin_cb, ctrl, desc_text, rc = agent_card_pkdecrypt (ctrl, kid, getpin_cb, ctrl, NULL,
ciphertext, ciphertextlen, ciphertext, ciphertextlen,
&plaintext, &plaintextlen, r_padding); &plaintext, &plaintextlen, r_padding);
if (!rc) if (!rc)

View File

@ -1082,6 +1082,104 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
return rc; return rc;
} }
/* Return the DISP-NAME without any padding characters. Caller must
* free the result. If not found or empty NULL is returned. */
static char *
get_disp_name (app_t app)
{
int rc;
void *relptr;
unsigned char *value;
size_t valuelen;
char *string;
char *p, *given;
char *result;
relptr = get_one_do (app, 0x005B, &value, &valuelen, &rc);
if (!relptr)
return NULL;
string = xtrymalloc (valuelen + 1);
if (!string)
{
xfree (relptr);
return NULL;
}
memcpy (string, value, valuelen);
string[valuelen] = 0;
xfree (relptr);
/* Swap surname and given name. */
given = strstr (string, "<<");
for (p = string; *p; p++)
if (*p == '<')
*p = ' ';
if (given && given[2])
{
*given = 0;
given += 2;
result = strconcat (given, " ", string, NULL);
}
else
{
result = string;
string = NULL;
}
xfree (string);
return result;
}
/* Return the pretty formatted serialnumber. On error NULL is
* returned. */
static char *
get_disp_serialno (app_t app)
{
char *serial = app_get_serialno (app);
/* For our OpenPGP cards we do not want to show the entire serial
* number but a nicely reformatted actual serial number. */
if (serial && strlen (serial) > 16+12)
{
memmove (serial, serial+16, 4);
serial[4] = ' ';
/* memmove (serial+5, serial+20, 4); */
/* serial[9] = ' '; */
/* memmove (serial+10, serial+24, 4); */
/* serial[14] = 0; */
memmove (serial+5, serial+20, 8);
serial[13] = 0;
}
return serial;
}
/* Return the number of remaining tries for the standard or the admin
* pw. Returns -1 on card error. */
static int
get_remaining_tries (app_t app, int adminpw)
{
void *relptr;
unsigned char *value;
size_t valuelen;
int remaining;
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
if (!relptr || valuelen < 7)
{
log_error (_("error retrieving CHV status from card\n"));
xfree (relptr);
return -1;
}
remaining = value[adminpw? 6 : 4];
xfree (relptr);
return remaining;
}
/* Retrieve the fingerprint from the card inserted in SLOT and write /* Retrieve the fingerprint from the card inserted in SLOT and write
the according hex representation to FPR. Caller must have provide the according hex representation to FPR. Caller must have provide
a buffer at FPR of least 41 bytes. Returns 0 on success or an a buffer at FPR of least 41 bytes. Returns 0 on success or an
@ -1874,6 +1972,62 @@ check_pinpad_request (app_t app, pininfo_t *pininfo, int admin_pin)
} }
/* Return a string with information about the card for use in a
* prompt. Returns NULL on memory failure. */
static char *
get_prompt_info (app_t app, int chvno, unsigned long sigcount, int remaining)
{
char *serial, *disp_name, *rembuf, *tmpbuf, *result;
serial = get_disp_serialno (app);
if (!serial)
return NULL;
disp_name = get_disp_name (app);
if (chvno == 1)
{
result = xtryasprintf (_("Card number:\t%s%%0A"
"Signatures:\t%lu%%0A"
"Cardholder:\t%s"),
serial,
sigcount,
disp_name? disp_name:"");
}
else
{
result = xtryasprintf (_("Card number:\t%s%%0A"
"Cardholder:\t%s"),
serial,
disp_name? disp_name:"");
}
xfree (disp_name);
xfree (serial);
if (remaining != -1)
{
/* TRANSLATORS: This is the number of remaining attempts to
* enter a PIN. Use %%0A (double-percent,0A) for a linefeed. */
rembuf = xtryasprintf (_("Remaining attempts: %d"), remaining);
if (!rembuf)
{
xfree (result);
return NULL;
}
tmpbuf = strconcat (result, "%0A%0A", rembuf, NULL);
xfree (rembuf);
if (!tmpbuf)
{
xfree (result);
return NULL;
}
xfree (result);
result = tmpbuf;
}
return result;
}
/* Verify a CHV either using the pinentry or if possible by /* Verify a CHV either using the pinentry or if possible by
using a pinpad. PINCB and PINCB_ARG describe the usual callback using a pinpad. PINCB and PINCB_ARG describe the usual callback
for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only
@ -1895,11 +2049,16 @@ verify_a_chv (app_t app,
const char *prompt; const char *prompt;
pininfo_t pininfo; pininfo_t pininfo;
int minlen = 6; int minlen = 6;
int remaining;
assert (chvno == 1 || chvno == 2); log_assert (chvno == 1 || chvno == 2);
*pinvalue = NULL; *pinvalue = NULL;
remaining = get_remaining_tries (app, 0);
if (remaining == -1)
return gpg_error (GPG_ERR_CARD);
if (chvno == 2 && app->app_local->flags.def_chv2) if (chvno == 2 && app->app_local->flags.def_chv2)
{ {
/* Special case for def_chv2 mechanism. */ /* Special case for def_chv2 mechanism. */
@ -1923,22 +2082,19 @@ verify_a_chv (app_t app,
pininfo.fixedlen = -1; pininfo.fixedlen = -1;
pininfo.minlen = minlen; pininfo.minlen = minlen;
{
const char *firstline = _("||Please unlock the card");
char *infoblock = get_prompt_info (app, chvno, sigcount,
remaining < 3? remaining : -1);
if (chvno == 1) prompt_buffer = strconcat (firstline, "%0A%0A", infoblock, NULL);
{ if (prompt_buffer)
#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, PROMPTSTRING, sigcount);
prompt = prompt_buffer; prompt = prompt_buffer;
#undef PROMPTSTRING else
} prompt = firstline; /* ENOMEM fallback. */
else
prompt = _("||Please enter the PIN");
xfree (infoblock);
}
if (!opt.disable_pinpad if (!opt.disable_pinpad
&& !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo)
@ -1961,7 +2117,7 @@ verify_a_chv (app_t app,
/* Dismiss the prompt. */ /* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL); pincb (pincb_arg, NULL, NULL);
assert (!*pinvalue); log_assert (!*pinvalue);
} }
else else
{ {
@ -2049,29 +2205,20 @@ verify_chv2 (app_t app,
static gpg_error_t static gpg_error_t
build_enter_admin_pin_prompt (app_t app, char **r_prompt) build_enter_admin_pin_prompt (app_t app, char **r_prompt)
{ {
void *relptr;
unsigned char *value;
size_t valuelen;
int remaining; int remaining;
char *prompt; char *prompt;
char *infoblock;
*r_prompt = NULL; *r_prompt = NULL;
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL); remaining = get_remaining_tries (app, 1);
if (!relptr || valuelen < 7) if (remaining == -1)
{ return gpg_error (GPG_ERR_CARD);
log_error (_("error retrieving CHV status from card\n")); if (!remaining)
xfree (relptr);
return gpg_error (GPG_ERR_CARD);
}
if (value[6] == 0)
{ {
log_info (_("card is permanently locked!\n")); log_info (_("card is permanently locked!\n"));
xfree (relptr);
return gpg_error (GPG_ERR_BAD_PIN); return gpg_error (GPG_ERR_BAD_PIN);
} }
remaining = value[6];
xfree (relptr);
log_info (ngettext("%d Admin PIN attempt remaining before card" log_info (ngettext("%d Admin PIN attempt remaining before card"
" is permanently locked\n", " is permanently locked\n",
@ -2079,16 +2226,13 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt)
" is permanently locked\n", " is permanently locked\n",
remaining), remaining); remaining), remaining);
if (remaining < 3) infoblock = get_prompt_info (app, 3, 0, remaining < 3? remaining : -1);
{
/* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
the start of the string. Use %%0A to force a linefeed. */
prompt = xtryasprintf (_("|A|Please enter the Admin PIN%%0A"
"[remaining attempts: %d]"), remaining);
}
else
prompt = xtrystrdup (_("|A|Please enter the Admin PIN"));
/* TRANSLATORS: Do not translate the "|A|" prefix but keep it at
the start of the string. Use %0A (single percent) for a linefeed. */
prompt = strconcat (_("|A|Please enter the Admin PIN"),
"%0A%0A", infoblock, NULL);
xfree (infoblock);
if (!prompt) if (!prompt)
return gpg_error_from_syserror (); return gpg_error_from_syserror ();