From eb3f014b5d57373b9eb7b3430c7afa115e2f871e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 27 Apr 2005 19:47:53 +0000 Subject: [PATCH] * app-p15.c (micardo_mse): New. (do_sign): Call it. * iso7816.c (iso7816_manage_security_env): Allow passing DATA as NULL to indicate an empty Lc. * tlv.c (find_tlv): Check that a found object fits into the buffer. (find_tlv_unchecked): New as replacement for the old non-checking variant. * app.c (select_application): Keep on using the non-checking variant. * app-openpgp.c (get_one_do, dump_all_do): Ditto. --- scd/ChangeLog | 13 +++++ scd/app-openpgp.c | 6 +- scd/app-p15.c | 142 ++++++++++++++++++++++++++++++++++++++++------ scd/app.c | 2 +- scd/iso7816.c | 7 ++- scd/tlv.c | 23 ++++++-- scd/tlv.h | 11 +++- 7 files changed, 175 insertions(+), 29 deletions(-) diff --git a/scd/ChangeLog b/scd/ChangeLog index 4ce4c656d..d82e92904 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,5 +1,18 @@ 2005-04-27 Werner Koch + * app-p15.c (micardo_mse): New. + (do_sign): Call it. + * iso7816.c (iso7816_manage_security_env): Allow passing DATA as + NULL to indicate an empty Lc. + * tlv.c (find_tlv): Check that a found object fits into the + buffer. + (find_tlv_unchecked): New as replacement for the old non-checking + variant. + * app.c (select_application): Keep on using the non-checking + variant. + * app-openpgp.c (get_one_do, dump_all_do): Ditto. + + Removal of the old OpenSC based code. * app-p15.c: New. Basic support for pkcs15 cards without OpenSC. diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 0d80c41a7..ca0e2501b 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -357,7 +357,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, { const unsigned char *s; - s = find_tlv (buffer, buflen, tag, &valuelen); + s = find_tlv_unchecked (buffer, buflen, tag, &valuelen); if (!s) value = NULL; /* not found */ else if (valuelen > buflen - (s - buffer)) @@ -433,8 +433,8 @@ dump_all_do (int slot) if (j==i || data_objects[i].tag != data_objects[j].get_from) continue; - value = find_tlv (buffer, buflen, - data_objects[j].tag, &valuelen); + value = find_tlv_unchecked (buffer, buflen, + data_objects[j].tag, &valuelen); if (!value) ; /* not found */ else if (valuelen > buflen - (value - buffer)) diff --git a/scd/app-p15.c b/scd/app-p15.c index 7a92da10b..d2ed15a59 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -59,11 +59,15 @@ static struct "\x90\x00\x66", CARD_TYPE_TCOS }, /* SLE66P */ { 27, "\x3B\xFF\x94\x00\xFF\x80\xB1\xFE\x45\x1F\x03\x00\x68\xD2\x76\x00" - "\x00\x28\xFF\x05\x1E\x31\x80\x00\x90\x00\x23", + "\x00\x28\xFF\x05\x1E\x31\x80\x00\x90\x00\x23", CARD_TYPE_MICARDO }, /* German BMI card */ + { 19, "\x3B\x6F\x00\xFF\x00\x68\xD2\x76\x00\x00\x28\xFF\x05\x1E\x31\x80" + "\x00\x90\x00", + CARD_TYPE_MICARDO }, /* German BMI card (ATR due to reader problem) */ { 26, "\x3B\xFE\x94\x00\xFF\x80\xB1\xFA\x45\x1F\x03\x45\x73\x74\x45\x49" "\x44\x20\x76\x65\x72\x20\x31\x2E\x30\x43", CARD_TYPE_MICARDO }, /* EstEID (Estonian Big Brother card) */ + { 0 } }; @@ -392,7 +396,7 @@ select_and_read_binary (int slot, unsigned short efid, const char *efid_desc, } -/* This function calls select file to read a file suing a complete +/* This function calls select file to read a file using a complete path which may or may not start at the master file (MF). */ static gpg_error_t select_ef_by_path (app_t app, const unsigned short *path, size_t pathlen) @@ -2525,6 +2529,99 @@ do_readcert (app_t app, const char *certid, } +/* Micardo cards require special treatment. This is a helper for the + crypto functions to manage the security environment. We expect that + the key file has already been selected. FID is the one of the + selected key. */ +static gpg_error_t +micardo_mse (app_t app, unsigned short fid) +{ + gpg_error_t err; + int recno; + unsigned short refdata = 0; + int se_num; + unsigned char msebuf[10]; + + /* Read the KeyD file containing extra information on keys. */ + err = iso7816_select_file (app->slot, 0x0013, 0, NULL, NULL); + if (err) + { + log_error ("error reading EF_keyD: %s\n", gpg_strerror (err)); + return err; + } + + for (recno = 1, se_num = -1; ; recno++) + { + unsigned char *buffer; + size_t buflen; + size_t n, nn; + const unsigned char *p, *pp; + + err = iso7816_read_record (app->slot, recno, 1, 0, &buffer, &buflen); + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) + break; /* ready */ + if (err) + { + log_error ("error reading EF_keyD record: %s\n", + gpg_strerror (err)); + return err; + } + log_printhex ("keyD record:", buffer, buflen); + p = find_tlv (buffer, buflen, 0x83, &n); + if (p && n == 4 && ((p[2]<<8)|p[3]) == fid) + { + refdata = ((p[0]<<8)|p[1]); + /* Locate the SE DO and the there included sec env number. */ + p = find_tlv (buffer, buflen, 0x7b, &n); + if (p && n) + { + pp = find_tlv (p, n, 0x80, &nn); + if (pp && nn == 1) + { + se_num = *pp; + xfree (buffer); + break; /* found. */ + } + } + } + xfree (buffer); + } + if (se_num == -1) + { + log_error ("CRT for keyfile %04hX not found\n", fid); + return gpg_error (GPG_ERR_NOT_FOUND); + } + + + /* Restore the security environment to SE_NUM if needed */ + if (se_num) + { + err = iso7816_manage_security_env (app->slot, 0xf3, se_num, NULL, 0); + if (err) + { + log_error ("restoring SE to %d failed: %s\n", + se_num, gpg_strerror (err)); + return err; + } + } + + /* Set the DST reference data. */ + msebuf[0] = 0x83; + msebuf[1] = 0x03; + msebuf[2] = 0x80; + msebuf[3] = (refdata >> 8); + msebuf[4] = refdata; + err = iso7816_manage_security_env (app->slot, 0x41, 0xb6, msebuf, 5); + if (err) + { + log_error ("setting SE to reference file %04hX failed: %s\n", + refdata, gpg_strerror (err)); + return err; + } + return 0; +} + + /* Handler for the PKSIGN command. @@ -2561,6 +2658,13 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); if (err) return err; + if (!(prkdf->usageflags.sign || prkdf->usageflags.sign_recover + ||prkdf->usageflags.non_repudiation)) + { + log_error ("key %s may not be used for signing\n", keyidstr); + return gpg_error (GPG_ERR_WRONG_KEY_USAGE); + } + if (!prkdf->authid) { log_error ("no authentication object defined for %s\n", keyidstr); @@ -2597,6 +2701,16 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, return gpg_error (GPG_ERR_INV_CARD); } + /* Select the key file. Note that this may change the security + environment thus we do it before PIN verification. */ + err = select_ef_by_path (app, prkdf->path, prkdf->pathlen); + if (err) + { + log_error ("error selecting file for key %s: %s\n", + keyidstr, gpg_strerror (errno)); + return err; + } + /* Now that we have all the information available, prepare and run the PIN verification.*/ if (1) @@ -2742,7 +2856,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, memcpy (data+15, indata, indatalen); } - /* Manage security environment needs to be weaked for certain cards. */ if (app->app_local->card_type == CARD_TYPE_TCOS) { @@ -2751,10 +2864,10 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, } else if (app->app_local->card_type == CARD_TYPE_MICARDO) { - /* Micardo cards are very special in that they need to restore a - security environment using a infomration from a special - file. */ - log_error ("WARNING: support for MICARDO cards is not yet available\n"); + if (!prkdf->pathlen) + err = gpg_error (GPG_ERR_BUG); + else + err = micardo_mse (app, prkdf->path[prkdf->pathlen-1]); } else if (prkdf->key_reference_valid) { @@ -2767,11 +2880,11 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, err = iso7816_manage_security_env (app->slot, 0x41, 0xB6, mse, sizeof mse); - if (err) - { - log_error ("MSE failed: %s\n", gpg_strerror (err)); - return err; - } + } + if (err) + { + log_error ("MSE failed: %s\n", gpg_strerror (err)); + return err; } @@ -2782,9 +2895,6 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, - - - /* Select the PKCS#15 application on the card in SLOT. */ gpg_error_t app_select_p15 (app_t app) @@ -2846,7 +2956,7 @@ app_select_p15 (app_t app) the common APP structure. */ app->app_local->card_type = card_type; - /* Read basic information and check whether this is a real + /* Read basic information and thus check whether this is a real card. */ rc = read_p15_info (app); if (rc) diff --git a/scd/app.c b/scd/app.c index b6d46326b..8e95ef7ef 100644 --- a/scd/app.c +++ b/scd/app.c @@ -83,7 +83,7 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) size_t n; const unsigned char *p; - p = find_tlv (result, resultlen, 0x5A, &n); + p = find_tlv_unchecked (result, resultlen, 0x5A, &n); if (p) resultlen -= (p-result); if (p && n > resultlen && n == 0x0d && resultlen+1 == n) diff --git a/scd/iso7816.c b/scd/iso7816.c index 9eff9d3f7..e9dc6541c 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -299,10 +299,11 @@ iso7816_manage_security_env (int slot, int p1, int p2, { int sw; - if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 || !data || !datalen) + if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 ) return gpg_error (GPG_ERR_INV_VALUE); - sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, datalen, data); + sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, + data? datalen : -1, data); return map_sw (sw); } @@ -605,7 +606,7 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef, buffer = NULL; bufferlen = 0; - /* Fixme: Either the ccid driver of the TCOS cards have problems + /* Fixme: Either the ccid driver or the TCOS cards have problems with an Le of 0. */ sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD, recno, diff --git a/scd/tlv.c b/scd/tlv.c index 5b9d0d6b9..3a81ea6d9 100644 --- a/scd/tlv.c +++ b/scd/tlv.c @@ -1,5 +1,5 @@ /* tlv.c - Tag-Length-Value Utilities - * Copyright (C) 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -113,17 +113,32 @@ do_find_tlv (const unsigned char *buffer, size_t length, /* Locate a TLV encoded data object in BUFFER of LENGTH and return a pointer to value as well as its length in NBYTES. Return - NULL if it was not found. Note, that the function does not check - whether the value fits into the provided buffer. */ + NULL if it was not found or if the object does not fit into the buffer. */ const unsigned char * find_tlv (const unsigned char *buffer, size_t length, int tag, size_t *nbytes) { - return do_find_tlv (buffer, length, tag, nbytes, 0); + const unsigned char *p; + + p = do_find_tlv (buffer, length, tag, nbytes, 0); + if (p && *nbytes > (length - (p-buffer))) + p = NULL; /* Object longer than buffer. */ + return p; } +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found. Note, that the function does not check + whether the value fits into the provided buffer. */ +const unsigned char * +find_tlv_unchecked (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + return do_find_tlv (buffer, length, tag, nbytes, 0); +} + /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag and the length part from the TLV triplet. Update BUFFER and SIZE diff --git a/scd/tlv.h b/scd/tlv.h index 26a9905f7..628580431 100644 --- a/scd/tlv.h +++ b/scd/tlv.h @@ -62,13 +62,20 @@ enum tlv_tag_type { }; +/* Locate a TLV encoded data object in BUFFER of LENGTH and return a + pointer to value as well as its length in NBYTES. Return NULL if + it was not found or if the object does not fit into the buffer. */ +const unsigned char *find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes); + /* Locate a TLV encoded data object in BUFFER of LENGTH and return a pointer to value as well as its length in NBYTES. Return NULL if it was not found. Note, that the function does not check whether the value fits into the provided buffer.*/ -const unsigned char *find_tlv (const unsigned char *buffer, size_t length, - int tag, size_t *nbytes); +const unsigned char *find_tlv_unchecked (const unsigned char *buffer, + size_t length, + int tag, size_t *nbytes); /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag