summaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2020-05-04 19:01:16 +0200
committerWerner Koch <wk@gnupg.org>2020-05-04 19:01:16 +0200
commit1e72a1a218490c0fc07811a02ddad6cc38913f77 (patch)
tree14d3b72566afba0d03b39fb04de6a2d1a73ef1b3 /scd
parentsm: Support encryption using ECDH keys. (diff)
downloadgnupg2-1e72a1a218490c0fc07811a02ddad6cc38913f77.tar.xz
gnupg2-1e72a1a218490c0fc07811a02ddad6cc38913f77.zip
scd:nks: Add do_with_keygrip and implement a cache.
* scd/app-nks.c (struct fid_cache_s): New. (struct app_local_s): Add field 'fid_cache'. (do_deinit): Release the cache. (keygripstr_from_pk_file): Implement the cache. (find_fid_by_keyref): New (do_sign, do_decipher): Use new function. (do_with_keygrip): New. Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'scd')
-rw-r--r--scd/app-nks.c439
1 files changed, 349 insertions, 90 deletions
diff --git a/scd/app-nks.c b/scd/app-nks.c
index 75dd106b5..ad061dffa 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -135,6 +135,14 @@ static struct
};
+/* Object to cache information gathred from FIDs. */
+struct fid_cache_s {
+ struct fid_cache_s *next;
+ int fid; /* Zero for an unused slot. */
+ unsigned int got_keygrip:1; /* The keygrip is valid. */
+ char keygripstr[2*KEYGRIP_LEN+1];
+};
+
/* Object with application (i.e. NKS) specific data. */
struct app_local_s {
@@ -149,6 +157,7 @@ struct app_local_s {
int need_app_select; /* Need to re-select the application. */
+ struct fid_cache_s *fid_cache; /* Linked list with cached infos. */
};
@@ -165,6 +174,13 @@ do_deinit (app_t app)
{
if (app && app->app_local)
{
+ while (app->app_local->fid_cache)
+ {
+ struct fid_cache_s *next = app->app_local->fid_cache->next;
+ xfree (app->app_local->fid_cache);
+ app->app_local->fid_cache = next;
+ }
+
xfree (app->app_local);
app->app_local = NULL;
}
@@ -197,9 +213,19 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
unsigned char grip[20];
unsigned char *buffer[2];
size_t buflen[2];
- gcry_sexp_t sexp;
+ gcry_sexp_t sexp = NULL;
int i;
int offset[2] = { 0, 0 };
+ struct fid_cache_s *ci;
+
+ for (ci = app->app_local->fid_cache; ci; ci = ci->next)
+ if (ci->fid && ci->fid == pkfid)
+ {
+ if (!ci->got_keygrip)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+ memcpy (r_gripstr, ci->keygripstr, 2*KEYGRIP_LEN+1);
+ return 0; /* Found in cache. */
+ }
if (app->app_local->nks_version == 15)
{
@@ -231,7 +257,7 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
log_error ("nks: error getting keygrip for certificate %04X: %s\n",
cfid, gpg_strerror (err));
- return err;
+ goto leave;
}
err = iso7816_select_file (app_get_slot (app), pkfid, 0);
@@ -327,11 +353,162 @@ keygripstr_from_pk_file (app_t app, int pkfid, int cfid, char *r_gripstr)
{
bin2hex (grip, 20, r_gripstr);
}
+
+
+ leave:
+ if (!err)
+ {
+ /* FIXME: We need to implement not_found caching. */
+ for (ci = app->app_local->fid_cache; ci; ci = ci->next)
+ if (ci->fid && ci->fid == pkfid)
+ {
+ /* Update the keygrip. */
+ ci->got_keygrip = 1;
+ memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1);
+ break;
+ }
+ if (!ci)
+ {
+ for (ci = app->app_local->fid_cache; ci; ci = ci->next)
+ if (!ci->fid)
+ break;
+ if (!ci)
+ ci = xtrycalloc (1, sizeof *ci);
+ if (!ci)
+ ; /* Out of memory - it is a cache, so we ignore it. */
+ else
+ {
+ ci->fid = pkfid;
+ memcpy (ci->keygripstr, r_gripstr, 2*KEYGRIP_LEN+1);
+ ci->got_keygrip = 1;
+ ci->next = app->app_local->fid_cache;
+ app->app_local->fid_cache = ci;
+ }
+ }
+ }
gcry_sexp_release (sexp);
return err;
}
+/* Parse KEYREF and return the index into the FILELIST at R_IDX.
+ * Returns 0 on success and switches to the requested application. */
+static gpg_error_t
+find_fid_by_keyref (app_t app, const char *keyref, int *r_idx)
+{
+ gpg_error_t err;
+ int idx, fid, nks_app_id;
+ char keygripstr[2*KEYGRIP_LEN+1];
+
+ if (!keyref || !keyref[0])
+ err = gpg_error (GPG_ERR_INV_ID);
+ else if (keyref[0] != 'N' && strlen (keyref) == 40) /* This is a keygrip. */
+ {
+ struct fid_cache_s *ci;
+
+ for (ci = app->app_local->fid_cache; ci; ci = ci->next)
+ if (ci->fid && ci->got_keygrip && !strcmp (ci->keygripstr, keygripstr))
+ break;
+ if (ci) /* Cached */
+ {
+ for (idx=0; filelist[idx].fid; idx++)
+ if (filelist[idx].fid == ci->fid)
+ break;
+ if (!filelist[idx].fid)
+ {
+ log_debug ("nks: Ooops: Unkown FID cached!\n");
+ err = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
+ err = switch_application (app, filelist[idx].nks_app_id);
+ if (err)
+ goto leave;
+ }
+ else /* Not cached. */
+ {
+ for (idx=0; filelist[idx].fid; idx++)
+ {
+ if (!filelist[idx].iskeypair)
+ continue;
+ if (filelist[idx].nks_app_id != NKS_APP_NKS
+ && filelist[idx].nks_app_id != app->app_local->qes_app_id)
+ continue;
+
+ err = switch_application (app, filelist[idx].nks_app_id);
+ if (err)
+ goto leave;
+
+ err = keygripstr_from_pk_file (app, filelist[idx].fid,
+ filelist[idx].iskeypair,
+ keygripstr);
+ if (err)
+ {
+ log_info ("nks: no keygrip for FID 0x%04X: %s\n",
+ filelist[idx].fid, gpg_strerror (err));
+ continue;
+ }
+ if (!strcmp (keygripstr, keyref))
+ break; /* Found */
+ }
+ if (!filelist[idx].fid)
+ {
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+ /* (No need to switch the app as that has already been done
+ * in the loop.) */
+ }
+ *r_idx = idx;
+ err = 0;
+ }
+ else /* This is a usual keyref. */
+ {
+ if (!ascii_strncasecmp (keyref, "NKS-NKS3.", 9))
+ nks_app_id = NKS_APP_NKS;
+ else if (!ascii_strncasecmp (keyref, "NKS-ESIGN.", 10)
+ && app->app_local->qes_app_id == NKS_APP_ESIGN)
+ nks_app_id = NKS_APP_ESIGN;
+ else if (!ascii_strncasecmp (keyref, "NKS-SIGG.", 9)
+ && app->app_local->qes_app_id == NKS_APP_SIGG)
+ nks_app_id = NKS_APP_SIGG;
+ else if (!ascii_strncasecmp (keyref, "NKS-DF01.", 9))
+ nks_app_id = NKS_APP_NKS;
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_ID);
+ goto leave;
+ }
+ keyref += nks_app_id == NKS_APP_ESIGN? 10 : 9;
+
+ if (!hexdigitp (keyref) || !hexdigitp (keyref+1)
+ || !hexdigitp (keyref+2) || !hexdigitp (keyref+3)
+ || keyref[4])
+ {
+ err = gpg_error (GPG_ERR_INV_ID);
+ goto leave;
+ }
+ fid = xtoi_4 (keyref);
+ for (idx=0; filelist[idx].fid; idx++)
+ if (filelist[idx].iskeypair && filelist[idx].fid == fid
+ && filelist[idx].nks_app_id == nks_app_id)
+ break;
+ if (!filelist[idx].fid)
+ {
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+ *r_idx = idx;
+
+ err = switch_application (app, nks_app_id);
+ if (err)
+ goto leave;
+ }
+
+ leave:
+ return err;
+}
+
+
/* TCOS responds to a verify with empty data (i.e. without the Lc
* byte) with the status of the PIN. PWID is the PIN ID. NKS_APP_ID
* gives the application to first switch to. Returns:
@@ -839,6 +1016,7 @@ do_writekey (app_t app, ctrl_t ctrl,
/* goto leave; */
/* Send the MSE:Store_Public_Key. */
+ /* We will need to clear the cache here. */
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
/* mse = xtrymalloc (1000); */
@@ -968,9 +1146,8 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
{ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
- int rc, i;
- int nks_app_id;
- int fid;
+ gpg_error_t err;
+ int idx;
unsigned char kid;
unsigned char data[83]; /* Must be large enough for a SHA-1 digest
+ the largest OID prefix. */
@@ -978,51 +1155,27 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
(void)ctrl;
- if (!keyidstr || !*keyidstr)
- return gpg_error (GPG_ERR_INV_VALUE);
switch (indatalen)
{
case 16: case 20: case 35: case 47: case 51: case 67: case 83: break;
default: return gpg_error (GPG_ERR_INV_VALUE);
}
- /* Check that the provided ID is valid. This is not really needed
- but we do it to enforce correct usage by the caller. */
- if (!strncmp (keyidstr, "NKS-NKS3.", 9))
- nks_app_id = NKS_APP_NKS;
- else if (!strncmp (keyidstr, "NKS-ESIGN.", 10))
- nks_app_id = NKS_APP_ESIGN;
- else if (!strncmp (keyidstr, "NKS-SIGG.", 9))
- nks_app_id = NKS_APP_SIGG;
- else if (!strncmp (keyidstr, "NKS-DF01.", 9))
- nks_app_id = NKS_APP_NKS;
- else
- return gpg_error (GPG_ERR_INV_ID);
- keyidstr += nks_app_id == NKS_APP_ESIGN? 10 : 9;
-
- rc = switch_application (app, nks_app_id);
- if (rc)
- return rc;
+ err = find_fid_by_keyref (app, keyidstr, &idx);
+ if (err)
+ return err;
- if (nks_app_id == NKS_APP_SIGG && app->app_local->sigg_is_msig)
+ if (app->app_local->active_nks_app == NKS_APP_SIGG
+ && app->app_local->sigg_is_msig)
{
log_info ("mass signature cards are not allowed\n");
return gpg_error (GPG_ERR_NOT_SUPPORTED);
}
- if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
- || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
- || keyidstr[4])
- return gpg_error (GPG_ERR_INV_ID);
- fid = xtoi_4 (keyidstr);
- for (i=0; filelist[i].fid; i++)
- if (filelist[i].iskeypair && filelist[i].fid == fid)
- break;
- if (!filelist[i].fid)
- return gpg_error (GPG_ERR_NOT_FOUND);
- if (!filelist[i].issignkey)
+ if (!filelist[idx].issignkey)
return gpg_error (GPG_ERR_INV_ID);
- kid = filelist[i].kid;
+
+ kid = filelist[idx].kid;
/* Prepare the DER object from INDATA. */
if (app->app_local->nks_version > 2 && (indatalen == 35
@@ -1076,17 +1229,17 @@ do_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
mse[3] = 0x84; /* Private key reference. */
mse[4] = 1;
mse[5] = kid;
- rc = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6,
- mse, sizeof mse);
+ err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB6,
+ mse, sizeof mse);
}
/* Verify using PW1.CH. */
- if (!rc)
- rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
+ if (!err)
+ err = verify_pin (app, 0, NULL, pincb, pincb_arg);
/* Compute the signature. */
- if (!rc)
- rc = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0,
- outdata, outdatalen);
- return rc;
+ if (!err)
+ err = iso7816_compute_ds (app_get_slot (app), 0, data, datalen, 0,
+ outdata, outdatalen);
+ return err;
}
@@ -1102,48 +1255,24 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
unsigned char **outdata, size_t *outdatalen,
unsigned int *r_info)
{
- int rc, i;
- int nks_app_id;
- int fid;
+ gpg_error_t err;
+ int idx;
int kid;
(void)ctrl;
(void)r_info;
- if (!keyidstr || !*keyidstr || !indatalen)
+ if (!indatalen)
return gpg_error (GPG_ERR_INV_VALUE);
- /* Check that the provided ID is valid. This is not really needed
- but we do it to enforce correct usage by the caller. */
- if (!strncmp (keyidstr, "NKS-NKS3.", 9))
- nks_app_id = NKS_APP_NKS;
- else if (!strncmp (keyidstr, "NKS-ESIGN.", 10))
- nks_app_id = NKS_APP_ESIGN;
- else if (!strncmp (keyidstr, "NKS-SIGG.", 9))
- nks_app_id = NKS_APP_SIGG;
- else if (!strncmp (keyidstr, "NKS-DF01.", 9))
- nks_app_id = NKS_APP_NKS;
- else
- return gpg_error (GPG_ERR_INV_ID);
- keyidstr += nks_app_id == NKS_APP_ESIGN? 10 : 9;
-
- rc = switch_application (app, nks_app_id);
- if (rc)
- return rc;
+ err = find_fid_by_keyref (app, keyidstr, &idx);
+ if (err)
+ return err;
- if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
- || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
- || keyidstr[4])
- return gpg_error (GPG_ERR_INV_ID);
- fid = xtoi_4 (keyidstr);
- for (i=0; filelist[i].fid; i++)
- if (filelist[i].iskeypair && filelist[i].fid == fid)
- break;
- if (!filelist[i].fid)
- return gpg_error (GPG_ERR_NOT_FOUND);
- if (!filelist[i].isencrkey)
+ if (!filelist[idx].isencrkey)
return gpg_error (GPG_ERR_INV_ID);
- kid = filelist[i].kid;
+
+ kid = filelist[idx].kid;
if (app->app_local->nks_version > 2)
{
@@ -1154,8 +1283,8 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
mse[3] = 0x84; /* Private key reference. */
mse[4] = 1;
mse[5] = kid;
- rc = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
- mse, sizeof mse);
+ err = iso7816_manage_security_env (app_get_slot (app), 0x41, 0xB8,
+ mse, sizeof mse);
}
else
{
@@ -1164,22 +1293,22 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
0x80, 1, 0x10, /* Select algorithm RSA. */
0x84, 1, 0x81 /* Select local secret key 1 for decryption. */
};
- rc = iso7816_manage_security_env (app_get_slot (app), 0xC1, 0xB8,
- mse, sizeof mse);
+ err = iso7816_manage_security_env (app_get_slot (app), 0xC1, 0xB8,
+ mse, sizeof mse);
}
- if (!rc)
- rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
+ if (!err)
+ err = verify_pin (app, 0, NULL, pincb, pincb_arg);
/* Note that we need to use extended length APDUs for TCOS 3 cards.
Command chaining does not work. */
- if (!rc)
- rc = iso7816_decipher (app_get_slot (app),
- app->app_local->nks_version > 2? 1:0,
- indata, indatalen, 0, 0x81,
- outdata, outdatalen);
- return rc;
+ if (!err)
+ err = iso7816_decipher (app_get_slot (app),
+ app->app_local->nks_version > 2? 1:0,
+ indata, indatalen, 0, 0x81,
+ outdata, outdatalen);
+ return err;
}
@@ -1412,6 +1541,135 @@ do_check_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
}
+/* Process the various keygrip based info requests. */
+static gpg_error_t
+do_with_keygrip (app_t app, ctrl_t ctrl, int action,
+ const char *want_keygripstr, int capability)
+{
+ gpg_error_t err;
+ char keygripstr[2*KEYGRIP_LEN+1];
+ char *serialno = NULL;
+ char idbuf[20];
+ int data = 0;
+ int idx;
+ const char *tagstr;
+
+ /* First a quick check for valid parameters. */
+ switch (action)
+ {
+ case KEYGRIP_ACTION_LOOKUP:
+ if (!want_keygripstr)
+ {
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ goto leave;
+ }
+ break;
+ case KEYGRIP_ACTION_SEND_DATA:
+ data = 1;
+ break;
+ case KEYGRIP_ACTION_WRITE_STATUS:
+ break;
+ default:
+ err = gpg_error (GPG_ERR_INV_ARG);
+ goto leave;
+ }
+
+ /* Allocate the S/N string if needed. */
+ if (action != KEYGRIP_ACTION_LOOKUP)
+ {
+ serialno = app_get_serialno (app);
+ if (!serialno)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ for (idx=0; filelist[idx].fid; idx++)
+ {
+ if (filelist[idx].nks_ver > app->app_local->nks_version)
+ continue;
+
+ if (!filelist[idx].iskeypair)
+ continue;
+
+ if (filelist[idx].nks_app_id != NKS_APP_NKS
+ && filelist[idx].nks_app_id != app->app_local->qes_app_id)
+ continue;
+
+ err = switch_application (app, filelist[idx].nks_app_id);
+ if (err)
+ goto leave;
+
+ err = keygripstr_from_pk_file (app, filelist[idx].fid,
+ filelist[idx].iskeypair, keygripstr);
+ if (err)
+ {
+ log_error ("can't get keygrip from FID 0x%04X: %s\n",
+ filelist[idx].fid, gpg_strerror (err));
+ continue;
+ }
+
+ if (action == KEYGRIP_ACTION_LOOKUP)
+ {
+ if (!strcmp (keygripstr, want_keygripstr))
+ {
+ err = 0; /* Found */
+ goto leave;
+ }
+ }
+ else if (!want_keygripstr || !strcmp (keygripstr, want_keygripstr))
+ {
+ if (capability == GCRY_PK_USAGE_SIGN)
+ {
+ if (!filelist[idx].issignkey)
+ continue;
+ }
+ if (capability == GCRY_PK_USAGE_ENCR)
+ {
+ if (!filelist[idx].isencrkey)
+ continue;
+ }
+ if (capability == GCRY_PK_USAGE_AUTH)
+ {
+ if (!filelist[idx].isauthkey)
+ continue;
+ }
+
+ if (app->app_local->active_nks_app == NKS_APP_ESIGN)
+ tagstr = "ESIGN";
+ else if (app->app_local->active_nks_app == NKS_APP_SIGG)
+ tagstr = "SIGG";
+ else if (app->app_local->nks_version < 3)
+ tagstr = "DF01";
+ else
+ tagstr = "NKS3";
+ snprintf (idbuf, sizeof idbuf, "NKS-%s.%04X",
+ tagstr, filelist[idx].fid);
+ send_keyinfo (ctrl, data, keygripstr, serialno, idbuf);
+ if (want_keygripstr)
+ {
+ err = 0; /* Found */
+ goto leave;
+ }
+ }
+ }
+
+ /* Return an error so that the dispatcher keeps on looping over the
+ * other applications. For clarity we use a different error code
+ * when listing all keys. Note that in lookup mode WANT_KEYGRIPSTR
+ * is not NULL. */
+ if (!want_keygripstr)
+ err = gpg_error (GPG_ERR_TRUE);
+ else
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+
+ leave:
+ xfree (serialno);
+ return err;
+}
+
+
/* Return the version of the NKS application. */
static int
get_nks_version (int slot)
@@ -1569,6 +1827,7 @@ app_select_nks (app_t app)
app->fnc.decipher = do_decipher;
app->fnc.change_pin = do_change_pin;
app->fnc.check_pin = do_check_pin;
+ app->fnc.with_keygrip = do_with_keygrip;
}
leave: