diff options
author | Werner Koch <[email protected]> | 2020-07-02 16:35:34 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2022-10-20 10:22:07 +0000 |
commit | fe698586b5d4b14fecf0295945f341e1de795c71 (patch) | |
tree | e30ec2b9cdbca6f073bbbb2e4a4463b808cb4b40 | |
parent | scd:nks: Fix certificate read problem with TCOS signature card v2. (diff) | |
download | gnupg-fe698586b5d4b14fecf0295945f341e1de795c71.tar.gz gnupg-fe698586b5d4b14fecf0295945f341e1de795c71.zip |
scd:nks: Implement writecert for the Signature card v2.
* scd/iso7816.c (CMD_UPDATE_BINARY): New.
(iso7816_update_binary): New.
* scd/app-nks.c (do_deinit): Factor some code out to...
(flush_fid_cache): new.
(do_writecert): New.
(app_select_nks): Register new handler.
--
This has been backported only to make the following backpoorts easier.
The code is only used in 2.3; for details see the original commit
message.
Signed-off-by: Werner Koch <[email protected]>
Backported-from-master: c1663c690b29d2dea8bc782c42de5eca08a24cc9
GnuPG-bug-id: 6252
-rw-r--r-- | scd/app-nks.c | 118 | ||||
-rw-r--r-- | scd/iso7816.c | 22 | ||||
-rw-r--r-- | scd/iso7816.h | 2 |
3 files changed, 134 insertions, 8 deletions
diff --git a/scd/app-nks.c b/scd/app-nks.c index b07dcdefc..df176ed7a 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; } @@ -1089,6 +1100,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 @@ -2196,6 +2297,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 c878a03c0..941b68d38 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -56,6 +56,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) @@ -1025,6 +1026,7 @@ iso7816_read_record_ext (int slot, int recno, int reccount, int short_ef, return 0; } + gpg_error_t iso7816_read_record (int slot, int recno, int reccount, int short_ef, unsigned char **result, size_t *resultlen) @@ -1032,3 +1034,23 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef, return iso7816_read_record_ext (slot, recno, reccount, short_ef, result, resultlen, NULL); } + + +/* 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 d22f0bec5..c45ea4210 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -150,5 +150,7 @@ gpg_error_t iso7816_read_record_ext (int slot, int recno, int reccount, 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*/ |