diff options
Diffstat (limited to 'scd/app-openpgp.c')
-rw-r--r-- | scd/app-openpgp.c | 122 |
1 files changed, 110 insertions, 12 deletions
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 5928ec620..9c85c61c9 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1,6 +1,6 @@ /* app-openpgp.c - The OpenPGP card application. * Copyright (C) 2003, 2004, 2005, 2007, 2008, - * 2009 Free Software Foundation, Inc. + * 2009, 2013 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -191,6 +191,14 @@ struct app_local_s { unsigned int def_chv2:1; /* Use 123456 for CHV2. */ } flags; + /* Keypad request specified on card. */ + struct + { + unsigned int specified:1; + int fixedlen_user; + int fixedlen_admin; + } keypad; + struct { unsigned int n_bits; /* Size of the modulus in bits. The rest @@ -581,17 +589,23 @@ count_bits (const unsigned char *a, size_t len) Everything up to a LF is considered a mailbox or account name. If the first LF is followed by DC4 (0x14) control sequence are expected up to the next LF. Control sequences are separated by FS - (0x18) and consist of key=value pairs. There is one key defined: + (0x18) and consist of key=value pairs. There are two keys defined: F=<flags> - Were FLAGS is a plain hexadecimal number representing flag values. + Where FLAGS is a plain hexadecimal number representing flag values. The lsb is here the rightmost bit. Defined flags bits are: Bit 0 = CHV1 and CHV2 are not syncronized Bit 1 = CHV2 has been been set to the default PIN of "123456" (this implies that bit 0 is also set). + P=<keypad-request> + + Where KEYPAD_REQUEST is 0 or a pair of two integers: <n>,<m>. + 0 means use keypad with variable length input. <n>,<m> means use + keypad with fixed length input. N for user PIN, M for admin PIN. + */ static void parse_login_data (app_t app) @@ -603,6 +617,9 @@ parse_login_data (app_t app) /* Set defaults. */ app->app_local->flags.no_sync = 0; app->app_local->flags.def_chv2 = 0; + app->app_local->keypad.specified = 0; + app->app_local->keypad.fixedlen_user = -1; + app->app_local->keypad.fixedlen_admin = -1; /* Read the DO. */ relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL); @@ -628,11 +645,56 @@ parse_login_data (app_t app) any leading digits but bail out on invalid characters. */ for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--) lastdig = xtoi_1 (p); + buffer = p; + buflen = len; if (len && !(*p == '\n' || *p == '\x18')) goto next; /* Invalid characters in field. */ app->app_local->flags.no_sync = !!(lastdig & 1); app->app_local->flags.def_chv2 = (lastdig & 3) == 3; } + else if (buflen > 1 && *buffer == 'P' && buffer[1] == '=') + { + /* Keypad request control sequence found. */ + buffer += 2; + buflen -= 2; + + if (buflen) + { + if (*buffer == '0') + { + buffer++; + buflen--; + if (buflen && !(*buffer == '\n' || *buffer == '\x18')) + goto next; + /* Disable use of pinpad. */ + app->app_local->keypad.specified = 1; + } + else if (digitp (buffer)) + { + char *q; + int n, m; + + n = strtol (buffer, &q, 10); + if (*q++ != ',' || !digitp (q)) + goto next; + m = strtol (q, &q, 10); + buffer = q; + if (buflen < ((unsigned char *)q - buffer)) + { + buflen = 0; + break; + } + else + buflen -= ((unsigned char *)q - buffer); + + if (buflen && !(*buffer == '\n' || *buffer == '\x18')) + goto next; + app->app_local->keypad.specified = 1; + app->app_local->keypad.fixedlen_user = n; + app->app_local->keypad.fixedlen_admin = m; + } + } + } next: for (; buflen && *buffer != '\x18'; buflen--, buffer++) if (*buffer == '\n') @@ -1470,6 +1532,39 @@ do_readcert (app_t app, const char *certid, } +/* Decide if we use the keypad of the reader for PIN input according + to the user preference on the card, and the capability of the + reader. This routine is only called when the reader has keypad. + Returns 0 if we use keypad, 1 otherwise. */ +static int +check_keypad_request (app_t app, pininfo_t *pininfo, int admin_pin) +{ + if (app->app_local->keypad.specified == 0) /* No preference on card. */ + if (pininfo->fixedlen == 0) /* Reader has varlen capability. */ + return 0; /* Then, use pinpad. */ + else + /* + * Reader has limited capability, and it may not match PIN of + * the card. + */ + return 1; + + if (admin_pin) + pininfo->fixedlen = app->app_local->keypad.fixedlen_admin; + else + pininfo->fixedlen = app->app_local->keypad.fixedlen_user; + + if (pininfo->fixedlen < 0 /* User requests disable pinpad. */ + || pininfo->fixedlen < pininfo->minlen + || pininfo->fixedlen > pininfo->maxlen + /* Reader doesn't have the capability to input a PIN which + * length is FIXEDLEN. */) + return 1; + + return 0; +} + + /* Verify a CHV either using using the pinentry or if possibile by using a keypad. PINCB and PINCB_ARG describe the usual callback for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only @@ -1489,7 +1584,7 @@ verify_a_chv (app_t app, int rc = 0; char *prompt_buffer = NULL; const char *prompt; - iso7816_pininfo_t pininfo; + pininfo_t pininfo; int minlen = 6; assert (chvno == 1 || chvno == 2); @@ -1516,7 +1611,7 @@ verify_a_chv (app_t app, } memset (&pininfo, 0, sizeof pininfo); - pininfo.mode = 1; + pininfo.fixedlen = -1; pininfo.minlen = minlen; @@ -1537,7 +1632,8 @@ verify_a_chv (app_t app, if (!opt.disable_keypad - && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) + && !check_keypad_request (app, &pininfo, 0)) { /* The reader supports the verify command through the keypad. Note that the pincb appends a text to the prompt telling the @@ -1707,12 +1803,12 @@ verify_chv3 (app_t app, if (!app->did_chv3) { - iso7816_pininfo_t pininfo; + pininfo_t pininfo; int minlen = 8; char *prompt; memset (&pininfo, 0, sizeof pininfo); - pininfo.mode = 1; + pininfo.fixedlen = -1; pininfo.minlen = minlen; rc = build_enter_admin_pin_prompt (app, &prompt); @@ -1720,7 +1816,8 @@ verify_chv3 (app_t app, return rc; if (!opt.disable_keypad - && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) + && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) + && !check_keypad_request (app, &pininfo, 1)) { /* The reader supports the verify command through the keypad. */ rc = pincb (pincb_arg, prompt, NULL); @@ -1917,13 +2014,13 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, char *pinvalue = NULL; int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET); int set_resetcode = 0; - iso7816_pininfo_t pininfo; + pininfo_t pininfo; int use_keypad = 0; int minlen = 6; (void)ctrl; memset (&pininfo, 0, sizeof pininfo); - pininfo.mode = 1; + pininfo.fixedlen = -1; pininfo.minlen = minlen; if (reset_mode && chvno == 3) @@ -1970,7 +2067,8 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, if (!opt.disable_keypad && !iso7816_check_keypad (app->slot, - ISO7816_CHANGE_REFERENCE_DATA, &pininfo)) + ISO7816_CHANGE_REFERENCE_DATA, &pininfo) + && !check_keypad_request (app, &pininfo, chvno == 3)) use_keypad = 1; if (reset_mode) |