aboutsummaryrefslogtreecommitdiffstats
path: root/scd/app-openpgp.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scd/app-openpgp.c276
1 files changed, 196 insertions, 80 deletions
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 1617ab888..6f9837c90 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -34,12 +34,12 @@
#include "errors.h"
#include "memory.h"
#include "util.h"
-#include "i18n.h"
#include "cardglue.h"
#else /* GNUPG_MAJOR_VERSION != 1 */
#include "scdaemon.h"
#endif /* GNUPG_MAJOR_VERSION != 1 */
+#include "i18n.h"
#include "iso7816.h"
#include "app-common.h"
#include "tlv.h"
@@ -52,27 +52,33 @@ static struct {
int binary;
int dont_cache;
int flush_on_error;
+ int get_immediate_in_v11; /* Enable a hack to bypass the cache of
+ this data object if it is used in 1.1
+ and later versions of the card. This
+ does not work with composite DO and is
+ currently only useful for the CHV
+ status bytes. */
char *desc;
} data_objects[] = {
- { 0x005E, 0, 0, 1, 0, 0, "Login Data" },
- { 0x5F50, 0, 0, 0, 0, 0, "URL" },
- { 0x0065, 1, 0, 1, 0, 0, "Cardholder Related Data"},
- { 0x005B, 0, 0x65, 0, 0, 0, "Name" },
- { 0x5F2D, 0, 0x65, 0, 0, 0, "Language preferences" },
- { 0x5F35, 0, 0x65, 0, 0, 0, "Sex" },
- { 0x006E, 1, 0, 1, 0, 0, "Application Related Data" },
- { 0x004F, 0, 0x6E, 1, 0, 0, "AID" },
- { 0x0073, 1, 0, 1, 0, 0, "Discretionary Data Objects" },
- { 0x0047, 0, 0x6E, 1, 0, 0, "Card Capabilities" },
- { 0x00C0, 0, 0x6E, 1, 0, 0, "Extended Card Capabilities" },
- { 0x00C1, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Signature" },
- { 0x00C2, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Decryption" },
- { 0x00C3, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Authentication" },
- { 0x00C4, 0, 0x6E, 1, 0, 1, "CHV Status Bytes" },
- { 0x00C5, 0, 0x6E, 1, 0, 0, "Fingerprints" },
- { 0x00C6, 0, 0x6E, 1, 0, 0, "CA Fingerprints" },
- { 0x007A, 1, 0, 1, 0, 0, "Security Support Template" },
- { 0x0093, 0, 0x7A, 1, 1, 0, "Digital Signature Counter" },
+ { 0x005E, 0, 0, 1, 0, 0, 0, "Login Data" },
+ { 0x5F50, 0, 0, 0, 0, 0, 0, "URL" },
+ { 0x0065, 1, 0, 1, 0, 0, 0, "Cardholder Related Data"},
+ { 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" },
+ { 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" },
+ { 0x5F35, 0, 0x65, 0, 0, 0, 0, "Sex" },
+ { 0x006E, 1, 0, 1, 0, 0, 0, "Application Related Data" },
+ { 0x004F, 0, 0x6E, 1, 0, 0, 0, "AID" },
+ { 0x0073, 1, 0, 1, 0, 0, 0, "Discretionary Data Objects" },
+ { 0x0047, 0, 0x6E, 1, 1, 0, 0, "Card Capabilities" },
+ { 0x00C0, 0, 0x6E, 1, 1, 0, 0, "Extended Card Capabilities" },
+ { 0x00C1, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Signature" },
+ { 0x00C2, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Decryption" },
+ { 0x00C3, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Authentication" },
+ { 0x00C4, 0, 0x6E, 1, 0, 1, 1, "CHV Status Bytes" },
+ { 0x00C5, 0, 0x6E, 1, 0, 0, 0, "Fingerprints" },
+ { 0x00C6, 0, 0x6E, 1, 0, 0, 0, "CA Fingerprints" },
+ { 0x007A, 1, 0, 1, 0, 0, 0, "Security Support Template" },
+ { 0x0093, 0, 0x7A, 1, 1, 0, 0, "Digital Signature Counter" },
{ 0 }
};
@@ -86,6 +92,13 @@ struct cache_s {
struct app_local_s {
struct cache_s *cache;
+ struct
+ {
+ unsigned int get_challenge:1;
+ unsigned int key_import:1;
+ unsigned int change_force_chv:1;
+ unsigned int private_dos:1;
+ } extcap;
};
@@ -124,24 +137,26 @@ get_cached_data (app_t app, int tag,
size_t len;
struct cache_s *c;
-
*result = NULL;
*resultlen = 0;
- if (app->app_local)
- {
- for (c=app->app_local->cache; c; c = c->next)
- if (c->tag == tag)
+ for (c=app->app_local->cache; c; c = c->next)
+ if (c->tag == tag)
+ {
+ if(c->length)
{
- p = xtrymalloc (c->length);
- if (!p)
- return gpg_error (gpg_err_code_from_errno (errno));
- memcpy (p, c->data, c->length);
- *resultlen = c->length;
- *result = p;
- return 0;
+ p = xtrymalloc (c->length);
+ if (!p)
+ return gpg_error (gpg_err_code_from_errno (errno));
+ memcpy (p, c->data, c->length);
+ *result = p;
}
- }
+
+ *resultlen = c->length;
+
+ return 0;
+ }
+
err = iso7816_get_data (app->slot, tag, &p, &len);
if (err)
@@ -159,24 +174,18 @@ get_cached_data (app_t app, int tag,
}
/* No, cache it. */
- if (!app->app_local)
- app->app_local = xtrycalloc (1, sizeof *app->app_local);
- /* Note that we can safely ignore out of core errors. */
- if (app->app_local)
+ for (c=app->app_local->cache; c; c = c->next)
+ assert (c->tag != tag);
+
+ c = xtrymalloc (sizeof *c + len);
+ if (c)
{
- for (c=app->app_local->cache; c; c = c->next)
- assert (c->tag != tag);
-
- c = xtrymalloc (sizeof *c + len);
- if (c)
- {
- memcpy (c->data, p, len);
- c->length = len;
- c->tag = tag;
- c->next = app->app_local->cache;
- app->app_local->cache = c;
- }
+ memcpy (c->data, p, len);
+ c->length = len;
+ c->tag = tag;
+ c->next = app->app_local->cache;
+ app->app_local->cache = c;
}
return 0;
@@ -202,7 +211,9 @@ flush_cache_item (app_t app, int tag)
xfree (c);
for (c=app->app_local->cache; c ; c = c->next)
- assert (c->tag != tag); /* Oops: duplicated entry. */
+ {
+ assert (c->tag != tag); /* Oops: duplicated entry. */
+ }
return;
}
@@ -262,6 +273,15 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes)
for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
;
+ if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
+ {
+ if( iso7816_get_data (app->slot, tag, &buffer, &buflen))
+ return NULL;
+ *result = buffer;
+ *nbytes = buflen;
+ return buffer;
+ }
+
value = NULL;
rc = -1;
if (data_objects[i].tag && data_objects[i].get_from)
@@ -428,7 +448,6 @@ store_fpr (int slot, int keynumber, u32 timestamp,
*p++ = nbits;
memcpy (p, e, elen); p += elen;
- log_printhex ("fprbuf:", buffer, n+3);
gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
xfree (buffer);
@@ -436,7 +455,22 @@ store_fpr (int slot, int keynumber, u32 timestamp,
rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6)
+ keynumber, fpr, 20);
if (rc)
- log_error ("failed to store the fingerprint: %s\n",gpg_strerror (rc));
+ log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
+
+ if (!rc && card_version > 0x0100)
+ {
+ unsigned char buf[4];
+
+ buf[0] = timestamp >> 24;
+ buf[1] = timestamp >> 16;
+ buf[2] = timestamp >> 8;
+ buf[3] = timestamp;
+
+ rc = iso7816_put_data (slot, 0xCE + keynumber, buf, 4);
+ if (rc)
+ log_error (_("failed to store the creation date: %s\n"),
+ gpg_strerror (rc));
+ }
return rc;
}
@@ -502,6 +536,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
{ "SIG-COUNTER", 0x0093, 2 },
{ "SERIALNO", 0x004F, -1 },
{ "AID", 0x004F },
+ { "EXTCAP", 0x0000, -2 },
{ NULL, 0 }
};
int idx, i;
@@ -536,6 +571,18 @@ do_getattr (APP app, CTRL ctrl, const char *name)
}
return 0;
}
+ if (table[idx].special == -2)
+ {
+ char tmp[50];
+
+ sprintf (tmp, "gc=%d ki=%d fc=%d pd=%d",
+ app->app_local->extcap.get_challenge,
+ app->app_local->extcap.key_import,
+ app->app_local->extcap.change_force_chv,
+ app->app_local->extcap.private_dos);
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ return 0;
+ }
relptr = get_one_do (app, table[idx].tag, &value, &valuelen);
if (relptr)
@@ -575,6 +622,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
static int
do_learn_status (APP app, CTRL ctrl)
{
+ do_getattr (app, ctrl, "EXTCAP");
do_getattr (app, ctrl, "DISP-NAME");
do_getattr (app, ctrl, "DISP-LANG");
do_getattr (app, ctrl, "DISP-SEX");
@@ -605,13 +653,14 @@ verify_chv2 (app_t app,
rc = pincb (pincb_arg, "PIN", &pinvalue);
if (rc)
{
- log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+ log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
return rc;
}
if (strlen (pinvalue) < 6)
{
- log_error ("prassphrase (CHV2) is too short; minimum length is 6\n");
+ log_error (_("prassphrase (CHV%d) is too short;"
+ " minimum length is %d\n"), 2, 6);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
@@ -619,7 +668,7 @@ verify_chv2 (app_t app,
rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
if (rc)
{
- log_error ("verify CHV2 failed: %s\n", gpg_strerror (rc));
+ log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
@@ -633,7 +682,7 @@ verify_chv2 (app_t app,
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
if (rc)
{
- log_error ("verify CHV1 failed: %s\n", gpg_strerror (rc));
+ log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
@@ -653,26 +702,54 @@ verify_chv3 (APP app,
{
int rc = 0;
+#if GNUPG_MAJOR_VERSION != 1
if (!opt.allow_admin)
{
- log_info ("access to admin commands is not configured\n");
+ log_info (_("access to admin commands is not configured\n"));
return gpg_error (GPG_ERR_EACCES);
}
+#endif
if (!app->did_chv3)
{
char *pinvalue;
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int reread_chv_status;
+
- rc = pincb (pincb_arg, "Admin PIN", &pinvalue);
+ relptr = get_one_do (app, 0x00C4, &value, &valuelen);
+ if (!relptr || valuelen < 7)
+ {
+ log_error (_("error retrieving CHV status from card\n"));
+ xfree (relptr);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ if (value[6] == 0)
+ {
+ log_info (_("card is permanently locked!\n"));
+ xfree (relptr);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ reread_chv_status = (value[6] < 3);
+
+ log_info(_("%d Admin PIN attempts remaining before card"
+ " is permanently locked\n"), value[6]);
+ xfree (relptr);
+
+ rc = pincb (pincb_arg, _("Admin PIN"), &pinvalue);
if (rc)
{
- log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+ log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
return rc;
}
if (strlen (pinvalue) < 6)
{
- log_error ("prassphrase (CHV3) is too short; minimum length is 6\n");
+ log_error (_("prassphrase (CHV%d) is too short;"
+ " minimum length is %d\n"), 3, 6);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
@@ -681,11 +758,18 @@ verify_chv3 (APP app,
xfree (pinvalue);
if (rc)
{
- log_error ("verify CHV3 failed: %s\n", gpg_strerror (rc));
+ log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc));
flush_cache_after_error (app);
return rc;
}
app->did_chv3 = 1;
+ /* If the PIN has been entered wrongly before, we need to flush
+ the cached value so that the next read correctly reflects the
+ resetted retry counter. Note that version 1.1 of the specs
+ allow direct reading of that DO, so that we could actually
+ flush it in all cases. */
+ if (reread_chv_status)
+ flush_cache_item (app, 0x00C4);
}
return rc;
}
@@ -1149,7 +1233,8 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
if (strlen (pinvalue) < 6)
{
- log_error ("prassphrase (CHV1) is too short; minimum length is 6\n");
+ log_error (_("prassphrase (CHV%d) is too short;"
+ " minimum length is %d\n"), 1, 6);
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
@@ -1157,7 +1242,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
if (rc)
{
- log_error ("verify CHV1 failed\n");
+ log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
@@ -1171,7 +1256,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
if (rc)
{
- log_error ("verify CHV2 failed\n");
+ log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
@@ -1375,11 +1460,14 @@ app_select_openpgp (APP app)
rc = iso7816_select_application (slot, aid, sizeof aid);
if (!rc)
{
+ unsigned int manufacturer;
+
app->apptype = "OPENPGP";
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
+ app->app_local = NULL;
/* The OpenPGP card returns the serial number as part of the
AID; because we prefer to use OpenPGP serial numbers, we
@@ -1391,33 +1479,57 @@ app_select_openpgp (APP app)
goto leave;
if (opt.verbose)
{
- log_info ("got AID: ");
+ log_info ("AID: ");
log_printhex ("", buffer, buflen);
}
-#if GNUPG_MAJOR_VERSION != 1
- /* A valid OpenPGP card should never need this but well the test
- is cheap. */
- rc = app_munge_serialno (app);
- if (rc)
- goto leave;
-#endif
app->card_version = buffer[6] << 8;
app->card_version |= buffer[7];
+ manufacturer = (buffer[8]<<8 | buffer[9]);
+
xfree (app->serialno);
app->serialno = buffer;
app->serialnolen = buflen;
buffer = NULL;
+ app->app_local = xtrycalloc (1, sizeof *app->app_local);
+ if (!app->app_local)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ goto leave;
+ }
relptr = get_one_do (app, 0x00C4, &buffer, &buflen);
if (!relptr)
{
- log_error ("can't access CHV Status Bytes - invalid OpenPGP card?\n");
+ log_error (_("can't access CHV Status Bytes "
+ "- invalid OpenPGP card?\n"));
goto leave;
}
app->force_chv1 = (buflen && *buffer == 0);
xfree (relptr);
-
+
+ relptr = get_one_do (app, 0x00C0, &buffer, &buflen);
+ if (!relptr)
+ {
+ log_error (_("can't access Extended Capability Flags - "
+ "invalid OpenPGP card?\n"));
+ goto leave;
+ }
+ if (buflen)
+ {
+ app->app_local->extcap.get_challenge = !!(*buffer & 0x40);
+ app->app_local->extcap.key_import = !!(*buffer & 0x20);
+ app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
+ app->app_local->extcap.private_dos = !!(*buffer & 0x08);
+ }
+ xfree (relptr);
+
+ /* Some of the first cards accidently don't set the
+ CHANGE_FORCE_CHV bit but allow it anyway. */
+ if (app->card_version <= 0x0100 && manufacturer == 1)
+ app->app_local->extcap.change_force_chv = 1;
+
+
if (opt.verbose > 1)
dump_all_do (slot);
@@ -1435,6 +1547,8 @@ app_select_openpgp (APP app)
}
leave:
+ if (rc)
+ do_deinit (app);
return rc;
}
@@ -1467,7 +1581,8 @@ app_openpgp_cardinfo (APP app,
rc = app_get_serial_and_stamp (app, serialno, &dummy);
if (rc)
{
- log_error ("error getting serial number: %s\n", gpg_strerror (rc));
+ log_error (_("error getting serial number: %s\n"),
+ gpg_strerror (rc));
return rc;
}
}
@@ -1554,13 +1669,14 @@ app_openpgp_storekey (APP app, int keyno,
if (rc)
goto leave;
+ flush_cache (app);
rc = iso7816_put_data (app->slot,
(app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
template, template_len);
if (rc)
{
- log_error ("failed to store the key: rc=%s\n", gpg_strerror (rc));
+ log_error (_("failed to store the key: %s\n"), gpg_strerror (rc));
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}
@@ -1602,14 +1718,14 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
if (rc)
{
rc = gpg_error (GPG_ERR_CARD);
- log_error ("reading key failed\n");
+ log_error (_("reading the key failed\n"));
goto leave;
}
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata)
{
- log_error ("response does not contain the public key data\n");
+ log_error (_("response does not contain the public key data\n"));
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}
@@ -1617,7 +1733,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
a = find_tlv (keydata, keydatalen, 0x0081, &alen);
if (!a)
{
- log_error ("response does not contain the RSA modulus\n");
+ log_error (_("response does not contain the RSA modulus\n"));
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}
@@ -1628,7 +1744,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
a = find_tlv (keydata, keydatalen, 0x0082, &alen);
if (!a)
{
- log_error ("response does not contain the RSA public exponent\n");
+ log_error (_("response does not contain the RSA public exponent\n"));
rc = gpg_error (GPG_ERR_CARD);
goto leave;
}