aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scd/app-nks.c118
-rw-r--r--scd/iso7816.c21
-rw-r--r--scd/iso7816.h2
3 files changed, 133 insertions, 8 deletions
diff --git a/scd/app-nks.c b/scd/app-nks.c
index e09015d55..5d326cb37 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -172,7 +172,7 @@ static struct
};
-/* Object to cache information gathred from FIDs. */
+/* Object to cache information gathered from FIDs. */
struct fid_cache_s {
struct fid_cache_s *next;
int fid; /* Zero for an unused slot. */
@@ -202,22 +202,33 @@ struct app_local_s {
static gpg_error_t readcert_from_ef (app_t app, int fid,
unsigned char **cert, size_t *certlen);
static gpg_error_t switch_application (app_t app, int nks_app_id);
+static const char *parse_pwidstr (app_t app, const char *pwidstr, int new_mode,
+ int *r_nks_app_id, int *r_pwid);
+static gpg_error_t verify_pin (app_t app, int pwid, const char *desc,
+ gpg_error_t (*pincb)(void*, const char *,
+ char **),
+ void *pincb_arg);
+static void
+flush_fid_cache (app_t app)
+{
+ 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;
+ }
+}
+
/* Release local data. */
static void
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;
- }
-
+ flush_fid_cache (app);
xfree (app->app_local);
app->app_local = NULL;
}
@@ -1095,6 +1106,96 @@ do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
}
+/* Write the certificate (CERT,CERTLEN) to the card at CERTREFSTR.
+ * CERTREFSTR is of the form "NKS_<yyy>.<four_hexdigit_keyref>". */
+static gpg_error_t
+do_writecert (app_t app, ctrl_t ctrl,
+ const char *certid,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *cert, size_t certlen)
+{
+ gpg_error_t err;
+ int i, fid, pwid;
+ int nks_app_id, tmp_app_id;
+ const char *desc;
+
+ (void)ctrl;
+
+ if (!strncmp (certid, "NKS-NKS3.", 9))
+ nks_app_id = NKS_APP_NKS;
+ else if (!strncmp (certid, "NKS-ESIGN.", 10))
+ nks_app_id = NKS_APP_ESIGN;
+ else if (!strncmp (certid, "NKS-SIGG.", 9))
+ nks_app_id = NKS_APP_SIGG;
+ else if (!strncmp (certid, "NKS-DF01.", 9))
+ nks_app_id = NKS_APP_NKS;
+ else if (!strncmp (certid, "NKS-IDLM.", 9))
+ nks_app_id = NKS_APP_IDLM;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+ certid += nks_app_id == NKS_APP_ESIGN? 10 : 9;
+
+ err = switch_application (app, nks_app_id);
+ if (err)
+ return err;
+
+ if (!hexdigitp (certid) || !hexdigitp (certid+1)
+ || !hexdigitp (certid+2) || !hexdigitp (certid+3)
+ || certid[4])
+ return gpg_error (GPG_ERR_INV_ID);
+ fid = xtoi_4 (certid);
+ for (i=0; filelist[i].fid; i++)
+ if ((filelist[i].certtype || filelist[i].iskeypair)
+ && filelist[i].nks_app_id == nks_app_id
+ && filelist[i].fid == fid)
+ break;
+ if (!filelist[i].fid)
+ return gpg_error (GPG_ERR_NOT_FOUND);
+
+ /* If the requested objects is a plain public key, redirect it to
+ * the corresponding certificate. This makes it easier for the user
+ * to figure out which CERTID to use. For example gpg-card shows
+ * the id of the key and not of the certificate. */
+ if (filelist[i].iskeypair)
+ fid = filelist[i].iskeypair;
+
+ /* We have no selective flush mechanism and given the rare use of
+ * writecert it won't harm to flush the entire cache. */
+ flush_fid_cache (app);
+
+
+ /* The certificates we support all require PW1.CH. Note that we
+ * check that the nks_app_id matches which sorts out CERTID values
+ * which are subkecy to a different nks_app_id. */
+ desc = parse_pwidstr (app, "PW1.CH", 0, &tmp_app_id, &pwid);
+ if (!desc || tmp_app_id != nks_app_id)
+ return gpg_error (GPG_ERR_INV_ID);
+ err = verify_pin (app, pwid, desc, pincb, pincb_arg);
+ if (err)
+ return err;
+
+ /* Select the file and write the certificate. */
+ err = iso7816_select_file (app_get_slot (app), fid, 0);
+ if (err)
+ {
+ log_error ("nks: error selecting FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ return err;
+ }
+
+ err = iso7816_update_binary (app_get_slot (app), 1, 0, cert, certlen);
+ if (err)
+ {
+ log_error ("nks: error updating certificate at FID 0x%04X: %s\n",
+ fid, gpg_strerror (err));
+ return err;
+ }
+
+ return 0;
+}
+
+
/* Handle the WRITEKEY command for NKS. This function expects a
canonical encoded S-expression with the public key in KEYDATA and
its length in KEYDATALEN. The only supported KEYID is
@@ -2208,6 +2309,7 @@ app_select_nks (app_t app)
app->fnc.readkey = do_readkey;
app->fnc.getattr = do_getattr;
app->fnc.setattr = NULL;
+ app->fnc.writecert = do_writecert;
app->fnc.writekey = do_writekey;
app->fnc.genkey = NULL;
app->fnc.sign = do_sign;
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 75aa8de3d..a841f77e0 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -45,6 +45,7 @@
#define CMD_GET_CHALLENGE 0x84
#define CMD_READ_BINARY 0xB0
#define CMD_READ_RECORD 0xB2
+#define CMD_UPDATE_BINARY 0xD6
static gpg_error_t
map_sw (int sw)
@@ -1018,3 +1019,23 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
return 0;
}
+
+
+/* Perform an UPDATE BINARY command on card in SLOT. Write DATA of
+ * length DATALEN to a transparent file at OFFSET. */
+gpg_error_t
+iso7816_update_binary (int slot, int extended_mode, size_t offset,
+ const void *data, size_t datalen)
+{
+ int sw;
+
+ /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+ * we check for this limit. */
+ if (offset > 32767)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_UPDATE_BINARY,
+ ((offset>>8) & 0xff), (offset & 0xff),
+ datalen, (const char*)data);
+ return map_sw (sw);
+}
diff --git a/scd/iso7816.h b/scd/iso7816.h
index b59103db2..10a1aa8d1 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -148,5 +148,7 @@ gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
int short_ef,
unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_update_binary (int slot, int extended_mode, size_t offset,
+ const void *data, size_t datalen);
#endif /*ISO7816_H*/