1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-09 23:39:51 +02:00

scd:nks: Support decryption using ECDH.

* scd/app-nks.c (struct fid_cache_s): Add field 'algo'.
(keygripstr_from_pk_file): Add arg 'r_algo' to return the algo.
(find_fid_by_keyref): Ditto.
(get_dispserialno): New.
(make_prompt): New.
(verify_pin): Provide better prompts.
(do_decipher): Support ECDH.
(parse_pwidstr): Add hack tospecify any pwid..
(do_change_pin): Support Signature Card V2.0 (NKS15) style NullPIN.
Provide a better prompt.
--

GnuPG-bug-id: 4938
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2020-05-07 08:18:28 +02:00
parent ee6d29f179
commit af45d884aa
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B

View File

@ -19,30 +19,53 @@
*/ */
/* Notes: /* Notes:
*
- We are now targeting TCOS 3 cards and it may happen that there is * - We are now targeting TCOS 3 cards and it may happen that there is
a regression towards TCOS 2 cards. Please report. * a regression towards TCOS 2 cards. Please report.
*
- The NKS3 AUT key is not used. It seems that it is only useful for * - The NKS3 AUT key is not used. It seems that it is only useful for
the internal authentication command and not accessible by other * the internal authentication command and not accessible by other
applications. The key itself is in the encryption class but the * applications. The key itself is in the encryption class but the
corresponding certificate has only the digitalSignature * corresponding certificate has only the digitalSignature
capability. * capability.
Update: This changed for the Signature Card V2 (nks version 15) * Update: This changed for the Signature Card V2 (nks version 15)
*
- If required, we automagically switch between the NKS application * - If required, we automagically switch between the NKS application
and the SigG or eSign application. This avoids to use the DINSIG * and the SigG or eSign application. This avoids to use the DINSIG
application which is somewhat limited, has no support for Secure * application which is somewhat limited, has no support for Secure
Messaging as required by TCOS 3 and has no way to change the PIN * Messaging as required by TCOS 3 and has no way to change the PIN
or even set the NullPIN. With the Signature Card v2 (nks version * or even set the NullPIN. With the Signature Card v2 (nks version
15) the Esign application is used instead of the SigG. * 15) the Esign application is used instead of the SigG.
*
- We use the prefix NKS-DF01 for TCOS 2 cards and NKS-NKS3 for newer * - We use the prefix NKS-DF01 for TCOS 2 cards and NKS-NKS3 for newer
cards. This is because the NKS application has moved to DF02 with * cards. This is because the NKS application has moved to DF02 with
TCOS 3 and thus we better use a DF independent tag. * TCOS 3 and thus we better use a DF independent tag.
*
- We use only the global PINs for the NKS application. * - We use only the global PINs for the NKS application.
*
*
*
* Here is a table with PIN stati collected from 3 cards.
*
* | app | pwid | NKS3 | SIG_B | SIG_N |
* |-----+------+-----------+-----------+-----------|
* | NKS | 0x00 | null - | - - | - - |
* | | 0x01 | 0 3 | - - | - - |
* | | 0x02 | 3 null | 15 3 | 15 null |
* | | 0x03 | - 3 | null - | null - |
* | SIG | 0x00 | null - | - - | - - |
* | | 0x01 | 0 null | - null | - null |
* | | 0x02 | 3 null | 15 0 | 15 0 |
* | | 0x03 | - 0 | null null | null null |
* - SIG is either SIGG or ESIGN.
* - "-" indicates reference not found (SW 6A88).
* - "null" indicates a NULLPIN (SW 6985).
* - The first value in each cell is the global PIN;
* the second is the local PIN (high bit of pwid set).
* - The NKS3 card is some older test card.
* - 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.
*/ */
#include <config.h> #include <config.h>
@ -139,7 +162,8 @@ static struct
struct fid_cache_s { struct fid_cache_s {
struct fid_cache_s *next; struct fid_cache_s *next;
int fid; /* Zero for an unused slot. */ int fid; /* Zero for an unused slot. */
unsigned int got_keygrip:1; /* The keygrip is valid. */ unsigned int got_keygrip:1; /* The keygrip and algo are valid. */
int algo;
char keygripstr[2*KEYGRIP_LEN+1]; char keygripstr[2*KEYGRIP_LEN+1];
}; };
@ -205,15 +229,18 @@ all_zero_p (void *buffer, size_t length)
* used and Read Record needs to be replaced by read binary. Given * used and Read Record needs to be replaced by read binary. Given
* all the ECC parameters required, we don't do that but rely that the * all the ECC parameters required, we don't do that but rely that the
* corresponding certificate at CFID is already available and get the * corresponding certificate at CFID is already available and get the
* public key from there. */ * public key from there. If R_ALGO is not NULL the public key
* algorithm for the returned KEYGRIP is stored there. */
static gpg_error_t static gpg_error_t
keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr) keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr,
int *r_algo)
{ {
gpg_error_t err; gpg_error_t err;
unsigned char grip[20]; unsigned char grip[20];
unsigned char *buffer[2]; unsigned char *buffer[2];
size_t buflen[2]; size_t buflen[2];
gcry_sexp_t sexp = NULL; gcry_sexp_t sexp = NULL;
int algo = 0; /* Public key algo. */
int i; int i;
int offset[2] = { 0, 0 }; int offset[2] = { 0, 0 };
struct fid_cache_s *ci; struct fid_cache_s *ci;
@ -224,6 +251,8 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
if (!ci->got_keygrip) if (!ci->got_keygrip)
return gpg_error (GPG_ERR_NOT_FOUND); return gpg_error (GPG_ERR_NOT_FOUND);
memcpy (r_gripstr, ci->keygripstr, 2*KEYGRIP_LEN+1); memcpy (r_gripstr, ci->keygripstr, 2*KEYGRIP_LEN+1);
if (r_algo)
*r_algo = ci->algo;
return 0; /* Found in cache. */ return 0; /* Found in cache. */
} }
@ -251,7 +280,7 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
return err; return err;
} }
err = app_help_get_keygrip_string_pk (pk, pklen, r_gripstr, NULL, NULL); err = app_help_get_keygrip_string_pk (pk, pklen, r_gripstr, NULL, &algo);
xfree (pk); xfree (pk);
if (err) if (err)
log_error ("nks: error getting keygrip for certificate %04X: %s\n", log_error ("nks: error getting keygrip for certificate %04X: %s\n",
@ -333,6 +362,7 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
} }
} }
algo = GCRY_PK_RSA;
if (!err) if (!err)
err = gcry_sexp_build (&sexp, NULL, err = gcry_sexp_build (&sexp, NULL,
"(public-key (rsa (n %b) (e %b)))", "(public-key (rsa (n %b) (e %b)))",
@ -352,6 +382,8 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
else else
{ {
bin2hex (grip, 20, r_gripstr); bin2hex (grip, 20, r_gripstr);
if (r_algo)
*r_algo = algo;
} }
@ -363,8 +395,9 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
if (ci->fid && ci->fid == pkfid) if (ci->fid && ci->fid == pkfid)
{ {
/* Update the keygrip. */ /* Update the keygrip. */
ci->got_keygrip = 1;
memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1); memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1);
ci->algo = algo;
ci->got_keygrip = 1;
break; break;
} }
if (!ci) if (!ci)
@ -380,6 +413,7 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
{ {
ci->fid = pkfid; ci->fid = pkfid;
memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1); memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1);
ci->algo = algo;
ci->got_keygrip = 1; ci->got_keygrip = 1;
ci->next = app->app_local->fid_cache; ci->next = app->app_local->fid_cache;
app->app_local->fid_cache = ci; app->app_local->fid_cache = ci;
@ -392,9 +426,10 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
/* Parse KEYREF and return the index into the FILELIST at R_IDX. /* Parse KEYREF and return the index into the FILELIST at R_IDX.
* Returns 0 on success and switches to the requested application. */ * Returns 0 on success and switches to the requested application.
* The public key algo is stored at R_ALGO unless it is NULL. */
static gpg_error_t static gpg_error_t
find_fid_by_keyref (app_t app, const char *keyref, int *r_idx) find_fid_by_keyref (app_t app, const char *keyref, int *r_idx, int *r_algo)
{ {
gpg_error_t err; gpg_error_t err;
int idx, fid, nks_app_id; int idx, fid, nks_app_id;
@ -407,7 +442,7 @@ find_fid_by_keyref (app_t app, const char *keyref, int *r_idx)
struct fid_cache_s *ci; struct fid_cache_s *ci;
for (ci = app->app_local->fid_cache; ci; ci = ci->next) for (ci = app->app_local->fid_cache; ci; ci = ci->next)
if (ci->fid && ci->got_keygrip && !strcmp (ci->keygripstr, keygripstr)) if (ci->fid && ci->got_keygrip && !strcmp (ci->keygripstr, keyref))
break; break;
if (ci) /* Cached */ if (ci) /* Cached */
{ {
@ -423,6 +458,8 @@ find_fid_by_keyref (app_t app, const char *keyref, int *r_idx)
err = switch_application (app, filelist[idx].nks_app_id); err = switch_application (app, filelist[idx].nks_app_id);
if (err) if (err)
goto leave; goto leave;
if (r_algo)
*r_algo = ci->algo;
} }
else /* Not cached. */ else /* Not cached. */
{ {
@ -440,10 +477,10 @@ find_fid_by_keyref (app_t app, const char *keyref, int *r_idx)
err = keygripstr_from_pk_file (app, filelist[idx].fid, err = keygripstr_from_pk_file (app, filelist[idx].fid,
filelist[idx].iskeypair, filelist[idx].iskeypair,
keygripstr); keygripstr, r_algo);
if (err) if (err)
{ {
log_info ("nks: no keygrip for FID 0x%04X: %s\n", log_info ("nks: no keygrip for FID 0x%04X: %s - ignored\n",
filelist[idx].fid, gpg_strerror (err)); filelist[idx].fid, gpg_strerror (err));
continue; continue;
} }
@ -502,6 +539,16 @@ find_fid_by_keyref (app_t app, const char *keyref, int *r_idx)
err = switch_application (app, nks_app_id); err = switch_application (app, nks_app_id);
if (err) if (err)
goto leave; goto leave;
if (r_algo)
{
/* We need to get the public key algo. */
err = keygripstr_from_pk_file (app, filelist[idx].fid,
filelist[idx].iskeypair,
keygripstr, r_algo);
if (err)
log_error ("nks: no keygrip for FID 0x%04X: %s\n",
filelist[idx].fid, gpg_strerror (err));
}
} }
leave: leave:
@ -679,7 +726,7 @@ do_learn_status_core (app_t app, ctrl_t ctrl, unsigned int flags,
int usageidx = 0; int usageidx = 0;
err = keygripstr_from_pk_file (app, filelist[i].fid, err = keygripstr_from_pk_file (app, filelist[i].fid,
filelist[i].iskeypair, gripstr); filelist[i].iskeypair, gripstr, NULL);
if (err) if (err)
log_error ("can't get keygrip from FID 0x%04X: %s\n", log_error ("can't get keygrip from FID 0x%04X: %s\n",
filelist[i].fid, gpg_strerror (err)); filelist[i].fid, gpg_strerror (err));
@ -1045,6 +1092,79 @@ 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 *
make_prompt (app_t app, int remaining, const char *firstline,
const char *extraline)
{
char *serial, *tmpbuf, *result;
serial = get_dispserialno (app);
/* TRANSLATORS: Put a \x1f right before a colon. This can be
* used by pinentry to nicely align the names and values. Keep
* the %s at the start and end of the string. */
result = xtryasprintf (_("%s"
"Number\x1f: %s%%0A"
"Holder\x1f: %s"
"%s"),
"\x1e",
serial,
"",
"");
xfree (serial);
if (!result)
return NULL; /* Out of core. */
/* Append a "remaining attempts" info if needed. */
if (remaining != -1 && remaining < 3)
{
char *rembuf;
/* 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)
{
tmpbuf = strconcat (firstline, "%0A%0A", result,
"%0A%0A", rembuf, NULL);
xfree (rembuf);
}
else
tmpbuf = NULL;
xfree (result);
result = tmpbuf;
}
else
{
tmpbuf = strconcat (firstline, "%0A%0A", result,
extraline? "%0A%0A":"", extraline,
NULL);
xfree (result);
result = tmpbuf;
}
return result;
}
static gpg_error_t static gpg_error_t
basic_pin_checks (const char *pinvalue, int minlen, int maxlen) basic_pin_checks (const char *pinvalue, int minlen, int maxlen)
{ {
@ -1068,21 +1188,43 @@ verify_pin (app_t app, int pwid, const char *desc,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg) void *pincb_arg)
{ {
pininfo_t pininfo;
int rc; int rc;
pininfo_t pininfo;
char *prompt;
const char *extrapromptline = NULL;
int remaining, nullpin;
if (!desc) if (!desc)
desc = "PIN"; desc = "||PIN";
memset (&pininfo, 0, sizeof pininfo); memset (&pininfo, 0, sizeof pininfo);
pininfo.fixedlen = -1; pininfo.fixedlen = -1;
pininfo.minlen = 6; pininfo.minlen = 6;
pininfo.maxlen = 16; pininfo.maxlen = 16;
remaining = iso7816_verify_status (app_get_slot (app), pwid);
nullpin = (remaining == ISO7816_VERIFY_NULLPIN);
if (remaining < 0)
remaining = -1; /* We don't care about the concrete error. */
if (remaining < 3)
{
if (remaining >= 0)
log_info ("nks: PIN has %d attempts left\n", remaining);
}
if (nullpin)
{
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!");
}
if (!opt.disable_pinpad if (!opt.disable_pinpad
&& !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) ) && !iso7816_check_pinpad (app_get_slot (app), ISO7816_VERIFY, &pininfo) )
{ {
rc = pincb (pincb_arg, desc, NULL); prompt = make_prompt (app, remaining, desc, extrapromptline);
rc = pincb (pincb_arg, prompt, NULL);
xfree (prompt);
if (rc) if (rc)
{ {
log_info (_("PIN callback returned error: %s\n"), log_info (_("PIN callback returned error: %s\n"),
@ -1097,7 +1239,9 @@ verify_pin (app_t app, int pwid, const char *desc,
{ {
char *pinvalue; char *pinvalue;
rc = pincb (pincb_arg, desc, &pinvalue); prompt = make_prompt (app, remaining, desc, extrapromptline);
rc = pincb (pincb_arg, prompt, &pinvalue);
xfree (prompt);
if (rc) if (rc)
{ {
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc)); log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
@ -1161,7 +1305,7 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
default: return gpg_error (GPG_ERR_INV_VALUE); default: return gpg_error (GPG_ERR_INV_VALUE);
} }
err = find_fid_by_keyref (app, keyidstr, &idx); err = find_fid_by_keyref (app, keyidstr, &idx, NULL);
if (err) if (err)
return err; return err;
@ -1258,6 +1402,10 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
gpg_error_t err; gpg_error_t err;
int idx; int idx;
int kid; int kid;
int algo;
int pwid;
int padind;
int extended_mode;
(void)ctrl; (void)ctrl;
(void)r_info; (void)r_info;
@ -1265,7 +1413,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
if (!indatalen) if (!indatalen)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
err = find_fid_by_keyref (app, keyidstr, &idx); err = find_fid_by_keyref (app, keyidstr, &idx, &algo);
if (err) if (err)
return err; return err;
@ -1274,7 +1422,30 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
kid = filelist[idx].kid; kid = filelist[idx].kid;
if (app->app_local->nks_version > 2) if (app->app_local->nks_version <= 2)
{
static const unsigned char mse[] =
{
0x80, 1, 0x10, /* Select algorithm RSA. */
0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
};
err = iso7816_manage_security_env (app_get_slot (app), 0xC1, 0xB8,
mse, sizeof mse);
extended_mode = 0;
padind = 0x81;
}
else if (algo == GCRY_PK_ECC)
{
unsigned char mse[3];
mse[0] = 0x84; /* Private key reference. */
mse[1] = 1;
mse[2] = kid;
err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
mse, sizeof mse);
extended_mode = 0;
padind = 0x00;
}
else
{ {
unsigned char mse[6]; unsigned char mse[6];
mse[0] = 0x80; /* Algorithm reference. */ mse[0] = 0x80; /* Algorithm reference. */
@ -1285,46 +1456,52 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
mse[5] = kid; mse[5] = kid;
err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8, err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
mse, sizeof mse); mse, sizeof mse);
extended_mode = 1;
padind = 0x81;
} }
else if (err)
{ {
static const unsigned char mse[] = log_error ("nks: MSE failed: %s\n", gpg_strerror (err));
{ goto leave;
0x80, 1, 0x10, /* Select algorithm RSA. */
0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
};
err = iso7816_manage_security_env (app_get_slot (app), 0xC1, 0xB8,
mse, sizeof mse);
} }
if (!err) if (app->app_local->nks_version == 15)
err = verify_pin (app, 0, NULL, pincb, pincb_arg); pwid = 0x03;
else
pwid = 0x00;
/* Note that we need to use extended length APDUs for TCOS 3 cards. err = verify_pin (app, pwid, NULL, pincb, pincb_arg);
Command chaining does not work. */ if (err)
if (!err) goto leave;
err = iso7816_decipher (app_get_slot (app),
app->app_local->nks_version > 2? 1:0, err = iso7816_decipher (app_get_slot (app), extended_mode,
indata, indatalen, 0, 0x81, indata, indatalen, 0, padind,
outdata, outdatalen); outdata, outdatalen);
leave:
return err; return err;
} }
/* Parse a password ID string. Returns NULL on error or a string /* Parse a password ID string. Returns NULL on error or a string
suitable as passphrase prompt on success. On success stores the * suitable as passphrase prompt on success. On success stores the
reference value for the password at R_PWID and a flag indicating * reference value for the password at R_PWID and a flag indicating
that the SigG application is to be used at R_SIGG. If NEW_MODE is * which app is to be used at R_NKS_APP_ID. If NEW_MODE is true, the
true, the returned description is suitable for a new Password. * returned description is suitable for a new Password. Supported
Supported values for PWIDSTR are: * values for PWIDSTR are:
*
PW1.CH - Global password 1 * PW1.CH - id 0x00 - Global password 1
PW2.CH - Global password 2 * PW2.CH - id 0x01 - Global password 2
PW1.CH.SIG - SigG password 1 * PW1.CH.SIG - id 0x81 - SigG password 1
PW2.CH.SIG - SigG password 2 * PW2.CH.SIG - id 0x83 - SigG password 2
FIXME: What about the ESIGN passwords? *
* It is also possible to specify the PIN id directly but the prompts
* are then not very descriptive (Use this for testing):
*
* 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 * static const char *
parse_pwidstr (const char *pwidstr, int new_mode parse_pwidstr (const char *pwidstr, int new_mode
@ -1374,6 +1551,36 @@ parse_pwidstr (const char *pwidstr, int new_mode
: _("|P|Please enter the PIN Unblocking Code (PUK) " : _("|P|Please enter the PIN Unblocking Code (PUK) "
"for the key to create qualified signatures.")); "for the key to create qualified signatures."));
} }
else if (!strncmp (pwidstr, "NKS.0x", 6)
&& hexdigitp (pwidstr+6) && hexdigitp (pwidstr+7) && !pwidstr[8])
{
/* Hack to help debugging. */
*r_nks_app_id = NKS_APP_NKS;
*r_pwid = xtoi_2 (pwidstr+6);
desc = (new_mode
? "|N|Please enter a new PIN for the given NKS pwid"
: "||Please enter the PIN for the given NKS pwid" );
}
else if (!strncmp (pwidstr, "SIGG.0x", 7)
&& hexdigitp (pwidstr+7) && hexdigitp (pwidstr+8) && !pwidstr[9])
{
/* Hack to help debugging. */
*r_nks_app_id = NKS_APP_SIGG;
*r_pwid = xtoi_2 (pwidstr+7);
desc = (new_mode
? "|N|Please enter a new PIN for the given SIGG pwid"
: "||Please enter the PIN for the given SIGG pwid" );
}
else if (!strncmp (pwidstr, "ESIGN.0x", 8)
&& hexdigitp (pwidstr+8) && hexdigitp (pwidstr+9) && !pwidstr[10])
{
/* Hack to help debugging. */
*r_nks_app_id = NKS_APP_ESIGN;
*r_pwid = xtoi_2 (pwidstr+8);
desc = (new_mode
? "|N|Please enter a new PIN for the given ESIGN pwid"
: "||Please enter the PIN for the given ESIGN pwid" );
}
else else
{ {
*r_pwid = 0; /* Only to avoid gcc warning in calling function. */ *r_pwid = 0; /* Only to avoid gcc warning in calling function. */
@ -1401,6 +1608,8 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
const char *newdesc; const char *newdesc;
int pwid; int pwid;
pininfo_t pininfo; pininfo_t pininfo;
int remaining;
char *prompt;
(void)ctrl; (void)ctrl;
@ -1421,6 +1630,15 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
if (err) if (err)
return err; return err;
remaining = iso7816_verify_status (app_get_slot (app), pwid);
if (remaining < 0)
remaining = -1; /* We don't care about the concrete error. */
if (remaining < 3)
{
if (remaining >= 0)
log_info ("nks: PIN has %d attempts left\n", remaining);
}
if ((flags & APP_CHANGE_FLAG_NULLPIN)) if ((flags & APP_CHANGE_FLAG_NULLPIN))
{ {
/* With the nullpin flag, we do not verify the PIN - it would /* With the nullpin flag, we do not verify the PIN - it would
@ -1431,7 +1649,15 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
goto leave; goto leave;
} }
oldpinlen = 6; if (app->app_local->nks_version == 15)
{
memset (oldpin, '0', 5);
oldpinlen = 5; /* 5 ascii zeroes. */
}
else
{
oldpinlen = 6; /* 6 binary Nuls. */
}
} }
else else
{ {
@ -1463,7 +1689,10 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
/* Regular change mode: Ask for the old PIN. */ /* Regular change mode: Ask for the old PIN. */
desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2); desc = parse_pwidstr (pwidstr, 0, &dummy1, &dummy2);
} }
err = pincb (pincb_arg, desc, &oldpin);
prompt = make_prompt (app, remaining, desc, NULL);
err = pincb (pincb_arg, prompt, &oldpin);
xfree (prompt);
if (err) if (err)
{ {
log_error ("error getting old PIN: %s\n", gpg_strerror (err)); log_error ("error getting old PIN: %s\n", gpg_strerror (err));
@ -1475,7 +1704,10 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
goto leave; goto leave;
} }
err = pincb (pincb_arg, newdesc, &newpin);
prompt = make_prompt (app, -1, newdesc, NULL);
err = pincb (pincb_arg, prompt, &newpin);
xfree (prompt);
if (err) if (err)
{ {
log_error (_("error getting new PIN: %s\n"), gpg_strerror (err)); log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
@ -1602,7 +1834,7 @@ do_with_keygrip (app_t app, ctrl_t ctrl, int action,
goto leave; goto leave;
err = keygripstr_from_pk_file (app, filelist[idx].fid, err = keygripstr_from_pk_file (app, filelist[idx].fid,
filelist[idx].iskeypair, keygripstr); filelist[idx].iskeypair, keygripstr, NULL);
if (err) if (err)
{ {
log_error ("can't get keygrip from FID 0x%04X: %s\n", log_error ("can't get keygrip from FID 0x%04X: %s\n",