From aecc008acb64ebbb6c667c4a128af4e61da57f84 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 7 May 2020 14:03:25 +0200 Subject: [PATCH] scd:nks: Get the PIN prompts right for the Signature Card * scd/app-nks.c (get_dispserialno): Move more to the top. (do_getattr): Add $DISPSERIALNO and SERIALNO. Make CHV-STATUS work with NKS15. (verify_pin): Use dedicated min. PIN lengths. (parse_pwidstr): Support NKS15 -- GnuPG-bug-id: 4938 Signed-off-by: Werner Koch --- scd/app-nks.c | 192 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 136 insertions(+), 56 deletions(-) diff --git a/scd/app-nks.c b/scd/app-nks.c index d571474fb..a172c29ef 100644 --- a/scd/app-nks.c +++ b/scd/app-nks.c @@ -52,7 +52,8 @@ * | NKS | 0x00 | null - | - - | - - | * | | 0x01 | 0 3 | - - | - - | * | | 0x02 | 3 null | 15 3 | 15 null | - * | | 0x03 | - 3 | null - | null - | + * | | 0x03 | - 3 | null - | 3 - | + * | | 0x04 | | null 0 | 3 3 | * | SIG | 0x00 | null - | - - | - - | * | | 0x01 | 0 null | - null | - null | * | | 0x02 | 3 null | 15 0 | 15 0 | @@ -66,6 +67,7 @@ * - The SIG_B is a Signature Card V2.0 with Brainpool curves. * Here the PIN 0x82 has been changed from the NULLPIN. * - The SIG_N is a Signature Card V2.0 with NIST curves. + * The PIN was enabled using the TCOS Windows tool. */ #include @@ -223,6 +225,22 @@ all_zero_p (void *buffer, size_t length) } +/* Return an allocated string with the serial number in a format to be + * show to the user. May return NULL on malloc problem. */ +static char * +get_dispserialno (app_t app) +{ + char *result; + + /* We only need to strip the last zero which is not printed on the + * card. */ + result = app_get_serialno (app); + if (result && *result && result[strlen(result)-1] == '0') + result[strlen(result)-1] = 0; + return result; +} + + /* Read the file with PKFID, assume it contains a public key and * return its keygrip in the caller provided 41 byte buffer R_GRIPSTR. * This works only for RSA card. For the Signature Card v2 ECC is @@ -587,10 +605,12 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) { "$SIGNKEYID", 3 }, { "NKS-VERSION", 4 }, { "CHV-STATUS", 5 }, - { NULL, 0 } + { "$DISPSERIALNO",6 }, + { "SERIALNO", 0 } }; gpg_error_t err = 0; int idx; + char *p, *p2; char buffer[100]; int nksver = app->app_local->nks_version; @@ -598,13 +618,25 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) if (err) return err; - for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) + for (idx=0; (idx < DIM(table) + && ascii_strcasecmp (table[idx].name, name)); idx++) ; - if (!table[idx].name) + if (!(idx < DIM (table))) return gpg_error (GPG_ERR_INV_NAME); switch (table[idx].special) { + case 0: /* SERIALNO */ + { + p = app_get_serialno (app); + if (p) + { + send_status_direct (ctrl, "SERIALNO", p); + xfree (p); + } + } + break; + case 1: /* $AUTHKEYID */ { /* NetKey 3.0 cards define an authentication key but according @@ -640,27 +672,58 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) case 5: /* 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. */ - /* FIXME: Check this for the NKS15!! */ + /* Return the status for the the PINs as described in the + * table below. See the macros ISO7816_VERIFY_* for a list + * for each slot. The order is + * + * | idx | name | + * |-----+------------| + * | 0 | PW1.CH | + * | 1 | PW2.CH | + * | 2 | PW1.CH.SIG | + * | 3 | PW2.CH.SIG | + * + * See parse_pwidstr for details of the mapping. + */ 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); + * no superfluous application switches. */ + if (app->app_local->nks_version == 15) + { + tmp[0] = get_chv_status (app, 0, 0x03); + tmp[1] = get_chv_status (app, 0, 0x04); + } + else + { + tmp[0] = get_chv_status (app, 0, 0x00); + tmp[1] = get_chv_status (app, 0, 0x01); + } tmp[2] = get_chv_status (app, app->app_local->qes_app_id, 0x81); - tmp[3] = get_chv_status (app, app->app_local->qes_app_id, 0x83); - snprintf (buffer, sizeof buffer, - "%d %d %d %d", tmp[0], tmp[1], tmp[2], tmp[3]); + if (app->app_local->nks_version == 15) + tmp[3] = get_chv_status (app, app->app_local->qes_app_id, 0x82); + else + tmp[3] = get_chv_status (app, app->app_local->qes_app_id, 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; + case 6: /* $DISPSERIALNO */ + { + p = app_get_serialno (app); + p2 = get_dispserialno (app); + if (p && p2 && strcmp (p, p2)) + send_status_info (ctrl, table[idx].name, p2, strlen (p2), + NULL, (size_t)0); + else /* No abbreviated S/N or identical to the full full S/N. */ + err = gpg_error (GPG_ERR_INV_NAME); /* No Abbreviated S/N. */ + xfree (p); + xfree (p2); + } + break; default: err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); @@ -1092,22 +1155,6 @@ do_writekey (app_t app, ctrl_t ctrl, } -/* Return an allocated string with the serial number in a format to be - * show to the user. May return NULL on malloc problem. */ -static char * -get_dispserialno (app_t app) -{ - char *result; - - /* We only need to strip the last zero which is not printed on the - * card. */ - result = app_get_serialno (app); - if (result && *result && result[strlen(result)-1] == '0') - result[strlen(result)-1] = 0; - return result; -} - - /* Return an allocated string to be used as prompt. Returns NULL on * malloc error. */ static char * @@ -1199,8 +1246,24 @@ verify_pin (app_t app, int pwid, const char *desc, memset (&pininfo, 0, sizeof pininfo); pininfo.fixedlen = -1; - pininfo.minlen = 6; - pininfo.maxlen = 16; + + /* FIXME: TCOS allows to read the min. and max. values - do this. */ + if (app->app_local->nks_version == 15) + { + if (app->app_local->active_nks_app == NKS_APP_NKS && pwid == 0x03) + pininfo.minlen = 6; + else if (app->app_local->active_nks_app == NKS_APP_ESIGN && pwid == 0x81) + pininfo.minlen = 6; + else + pininfo.minlen = 8; + pininfo.maxlen = 24; + } + else + { + /* For NKS3 we used these fixed values; let's keep this. */ + pininfo.minlen = 6; + pininfo.maxlen = 16; + } remaining = iso7816_verify_status (app_get_slot (app), pwid); nullpin = (remaining == ISO7816_VERIFY_NULLPIN); @@ -1216,7 +1279,7 @@ verify_pin (app_t app, int pwid, const char *desc, { log_info ("nks: The NullPIN for PIN 0x%02x has not yet been changed\n", pwid); - extrapromptline = _("Note: You need to change the PIN first!"); + extrapromptline = _("Note: PIN has not yet been enabled."); } if (!opt.disable_pinpad @@ -1292,6 +1355,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; gpg_error_t err; int idx; + int pwid; unsigned char kid; unsigned char data[83]; /* Must be large enough for a SHA-1 digest + the largest OID prefix. */ @@ -1376,9 +1440,15 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo, err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6, mse, sizeof mse); } - /* Verify using PW1.CH. */ + + /* We use the Global PIN 1 */ + if (app->app_local->nks_version == 15) + pwid = 0x03; + else + pwid = 0x00; + if (!err) - err = verify_pin (app, 0, NULL, pincb, pincb_arg); + err = verify_pin (app, pwid, NULL, pincb, pincb_arg); /* Compute the signature. */ if (!err) err = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0, @@ -1465,6 +1535,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, goto leave; } + /* We use the Global PIN 1 */ if (app->app_local->nks_version == 15) pwid = 0x03; else @@ -1488,33 +1559,42 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, * suitable as passphrase prompt on success. On success stores the * reference value for the password at R_PWID and a flag indicating * which app is to be used at R_NKS_APP_ID. If NEW_MODE is true, the - * returned description is suitable for a new Password. Supported - * values for PWIDSTR are: + * returned description is suitable for a new password. Here is a + * take mapping the PWIDSTR to the used PWIDs: * - * PW1.CH - id 0x00 - Global password 1 - * PW2.CH - id 0x01 - Global password 2 - * PW1.CH.SIG - id 0x81 - SigG password 1 - * PW2.CH.SIG - id 0x83 - SigG password 2 + * | pwidstr | | NKS3 | NKS15 | IDKEY1 | + * |------------+--------------+------+-------+--------| + * | PW1.CH | Global PIN 1 | 0x00 | 0x03 | 0x00 | + * | PW2.CH | Global PIN 2 | 0x01 | 0x04 | 0x01 | + * | PW1.CH.SIG | SigG PIN 1 | 0x81 | 0x81 | - | + * | PW2.CH.SIG | SigG PIN 2 | 0x83 | 0x82 | - | * - * It is also possible to specify the PIN id directly but the prompts - * are then not very descriptive (Use this for testing): + * The names for PWIDSTR are taken from the NKS3 specs; the specs of + * other cards use different names but we keep using the. PIN1 can be + * used to unlock PIN2 and vice versa; for consistence with other + * cards we name PIN2 a "PUK". The IDKEY card also features a Card + * Reset Key (CR Key 0x01) which can also be used to reset PIN1. + * + * For testing it is possible to specify the PWID directly; the + * prompts are then not very descriptive: * * NKS.0xnn - Switch to NKS and select id 0xnn * SIGG.0xnn - Switch to SigG and select id 0xnn * ESIGN.0xnn - Switch to ESIGN and select id 0xnn */ static const char * -parse_pwidstr (const char *pwidstr, int new_mode - , int *r_nks_app_id, int *r_pwid) +parse_pwidstr (app_t app, const char *pwidstr, int new_mode, + int *r_nks_app_id, int *r_pwid) { const char *desc; + int nks15 = app->app_local->nks_version == 15; if (!pwidstr) desc = NULL; else if (!strcmp (pwidstr, "PW1.CH")) { *r_nks_app_id = NKS_APP_NKS; - *r_pwid = 0x00; + *r_pwid = nks15? 0x03 : 0x00; /* TRANSLATORS: Do not translate the "|*|" prefixes but keep them verbatim at the start of the string. */ desc = (new_mode @@ -1524,7 +1604,7 @@ parse_pwidstr (const char *pwidstr, int new_mode else if (!strcmp (pwidstr, "PW2.CH")) { *r_nks_app_id = NKS_APP_NKS; - *r_pwid = 0x01; + *r_pwid = nks15? 0x04 : 0x01; desc = (new_mode ? _("|NP|Please enter a new PIN Unblocking Code (PUK) " "for the standard keys.") @@ -1533,7 +1613,7 @@ parse_pwidstr (const char *pwidstr, int new_mode } else if (!strcmp (pwidstr, "PW1.CH.SIG")) { - *r_nks_app_id = NKS_APP_SIGG; + *r_nks_app_id = app->app_local->qes_app_id; *r_pwid = 0x81; desc = (new_mode ? _("|N|Please enter a new PIN for the key to create " @@ -1543,8 +1623,8 @@ parse_pwidstr (const char *pwidstr, int new_mode } else if (!strcmp (pwidstr, "PW2.CH.SIG")) { - *r_nks_app_id = NKS_APP_SIGG; - *r_pwid = 0x83; /* Yes, that is 83 and not 82. */ + *r_nks_app_id = app->app_local->qes_app_id; + *r_pwid = nks15? 0x82 : 0x83; desc = (new_mode ? _("|NP|Please enter a new PIN Unblocking Code (PUK) " "for the key to create qualified signatures.") @@ -1619,7 +1699,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, pininfo.minlen = 6; pininfo.maxlen = 16; - newdesc = parse_pwidstr (pwidstr, 1, &nks_app_id, &pwid); + newdesc = parse_pwidstr (app, pwidstr, 1, &nks_app_id, &pwid); if (!newdesc) return gpg_error (GPG_ERR_INV_ID); @@ -1682,12 +1762,12 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr, err = gpg_error (GPG_ERR_BUG); goto leave; } - desc = parse_pwidstr (altpwidstr, 0, &dummy1, &dummy2); + desc = parse_pwidstr (app, altpwidstr, 0, &dummy1, &dummy2); } else { /* Regular change mode: Ask for the old PIN. */ - desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2); + desc = parse_pwidstr (app, pwidstr, 0, &dummy1, &dummy2); } prompt = make_prompt (app, remaining, desc, NULL); @@ -1761,7 +1841,7 @@ do_check_pin (app_t app, ctrl_t ctrl, const char *pwidstr, (void)ctrl; - desc = parse_pwidstr (pwidstr, 0, &nks_app_id, &pwid); + desc = parse_pwidstr (app, pwidstr, 0, &nks_app_id, &pwid); if (!desc) return gpg_error (GPG_ERR_INV_ID);