aboutsummaryrefslogtreecommitdiffstats
path: root/scd/app-piv.c
diff options
context:
space:
mode:
Diffstat (limited to 'scd/app-piv.c')
-rw-r--r--scd/app-piv.c273
1 files changed, 223 insertions, 50 deletions
diff --git a/scd/app-piv.c b/scd/app-piv.c
index 5748c70b8..3b94a28e4 100644
--- a/scd/app-piv.c
+++ b/scd/app-piv.c
@@ -19,6 +19,7 @@
/* Some notes:
* - Specs for PIV are at http://dx.doi.org/10.6028/NIST.SP.800-73-4
+ * - https://developers.yubico.com/PIV/Introduction/PIV_attestation.html
*
* - Access control matrix:
* | Action | 9B | PIN | PUK | |
@@ -60,7 +61,6 @@
#include "../common/util.h"
#include "../common/i18n.h"
#include "iso7816.h"
-#include "app-common.h"
#include "../common/tlv.h"
#include "../common/host2net.h"
#include "apdu.h" /* We use apdu_send_direct. */
@@ -81,6 +81,10 @@
#define PIV_ALGORITHM_ECC_P384 0x14
+/* The AID for PIV. */
+static char const piv_aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */
+ 0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ };
+
/* A table describing the DOs of a PIV card. */
struct data_object_s
@@ -282,7 +286,7 @@ get_cached_data (app_t app, int tag,
}
}
- err = iso7816_get_data_odd (app->slot, 0, tag, &p, &len);
+ err = iso7816_get_data_odd (app_get_slot (app), 0, tag, &p, &len);
if (err)
return err;
@@ -736,18 +740,18 @@ get_dispserialno (app_t app, int failmode)
{
char *result;
- if (app->serialno && app->serialnolen == 3+1+4
- && !memcmp (app->serialno, "\xff\x02\x00", 3))
+ if (app->card && app->card->serialno && app->card->serialnolen == 3+1+4
+ && !memcmp (app->card->serialno, "\xff\x02\x00", 3))
{
/* This is a 4 byte S/N of a Yubikey which seems to be printed
* on the token in decimal. Maybe they will print larger S/N
* also in decimal but we can't be sure, thus do it only for
* these 32 bit numbers. */
unsigned long sn;
- sn = app->serialno[4] * 16777216;
- sn += app->serialno[5] * 65536;
- sn += app->serialno[6] * 256;
- sn += app->serialno[7];
+ sn = app->card->serialno[4] * 16777216;
+ sn += app->card->serialno[5] * 65536;
+ sn += app->card->serialno[6] * 256;
+ sn += app->card->serialno[7];
result = xtryasprintf ("yk-%lu", sn);
}
else if (failmode)
@@ -785,7 +789,7 @@ get_chv_status (app_t app, const char *keyrefstr)
apdu[1] = ISO7816_VERIFY;
apdu[2] = 0x00;
apdu[3] = keyref;
- if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
+ if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL))
result = -5; /* No need to verification. */
else if (sw == 0x6a88 || sw == 0x6a80)
result = -2; /* No such PIN. */
@@ -811,7 +815,9 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
int special;
} table[] = {
{ "SERIALNO", 0x0000, -1 },
- { "$AUTHKEYID", 0x0000, -2 }, /* Default key for ssh. */
+ { "$AUTHKEYID", 0x0000, -2 }, /* Default ssh key. */
+ { "$ENCRKEYID", 0x0000, -6 }, /* Default encryption key. */
+ { "$SIGNKEYID", 0x0000, -7 }, /* Default signing key. */
{ "$DISPSERIALNO",0x0000, -3 },
{ "CHV-STATUS", 0x0000, -4 },
{ "CHV-USAGE", 0x007E, -5 }
@@ -882,6 +888,16 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
xfree (relptr);
}
}
+ else if (table[idx].special == -6)
+ {
+ char const tmp[] = "PIV.9D"; /* Key Management. */
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ }
+ else if (table[idx].special == -7)
+ {
+ char const tmp[] = "PIV.9C"; /* Digital Signature. */
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ }
else
{
relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &err);
@@ -925,7 +941,7 @@ auth_adm_key (app_t app, const unsigned char *value, size_t valuelen)
tmpl[2] = 0x80;
tmpl[3] = 0; /* (Empty witness requests a witness.) */
tmpllen = 4;
- err = iso7816_general_authenticate (app->slot, 0,
+ err = iso7816_general_authenticate (app_get_slot (app), 0,
PIV_ALGORITHM_3DES_ECB_0, 0x9B,
tmpl, tmpllen, 0,
&outdata, &outdatalen);
@@ -958,7 +974,7 @@ auth_adm_key (app_t app, const unsigned char *value, size_t valuelen)
tmpl[23] = 0;
tmpllen = 24;
xfree (outdata);
- err = iso7816_general_authenticate (app->slot, 0,
+ err = iso7816_general_authenticate (app_get_slot (app), 0,
PIV_ALGORITHM_3DES_ECB_0, 0x9B,
tmpl, tmpllen, 0,
&outdata, &outdatalen);
@@ -1032,7 +1048,8 @@ set_adm_key (app_t app, const unsigned char *value, size_t valuelen)
apdu[6] = 0x9b;
apdu[7] = 24;
memcpy (apdu+8, value, 24);
- err = iso7816_apdu_direct (app->slot, apdu, 8+24, 0, &sw, NULL, NULL);
+ err = iso7816_apdu_direct (app_get_slot (app), apdu, 8+24, 0,
+ &sw, NULL, NULL);
wipememory (apdu+8, 24);
if (err)
log_error ("piv: setting admin key failed; sw=%04x\n", sw);
@@ -1413,7 +1430,7 @@ do_readcert (app_t app, const char *certid,
apdu[1] = 0xf9; /* Yubikey: Get attestation cert. */
apdu[2] = xtoi_2 (certid+9);
apdu[3] = 0;
- err = iso7816_apdu_direct (app->slot, apdu, 4, 1,
+ err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 1,
NULL, &result, &resultlen);
if (!err)
{
@@ -1452,7 +1469,7 @@ do_readcert (app_t app, const char *certid,
* returned.
*/
static gpg_error_t
-do_readkey (app_t app, const char *keyrefstr,
+do_readkey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags,
unsigned char **r_pk, size_t *r_pklen)
{
gpg_error_t err;
@@ -1504,9 +1521,35 @@ do_readkey (app_t app, const char *keyrefstr,
goto leave;
}
- *r_pk = pk;
- pk = NULL;
- *r_pklen = pklen;
+ if ((flags & APP_READKEY_FLAG_INFO))
+ {
+ char keygripstr[KEYGRIP_LEN*2+1];
+ char idbuf[50];
+ const char *usage;
+
+ err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr);
+ if (err)
+ {
+ log_error ("app_help_get_keygrip_string_pk failed: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ usage = dobj->usage? dobj->usage : "";
+
+ snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref);
+ send_status_info (ctrl, "KEYPAIRINFO",
+ keygripstr, strlen (keygripstr),
+ idbuf, strlen (idbuf),
+ usage, strlen (usage),
+ NULL, (size_t)0);
+ }
+
+ if (r_pk && r_pklen)
+ {
+ *r_pk = pk;
+ pk = NULL;
+ *r_pklen = pklen;
+ }
leave:
gcry_sexp_release (s_pkey);
@@ -1841,7 +1884,7 @@ verify_chv (app_t app, int keyref, int force,
apdu[1] = ISO7816_VERIFY;
apdu[2] = 0x00;
apdu[3] = keyref;
- if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
+ if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL))
{
if (!force) /* No need to verification. */
return 0; /* All fine. */
@@ -1857,7 +1900,7 @@ verify_chv (app_t app, int keyref, int force,
if (err)
return err;
- err = iso7816_verify (app->slot, keyref, pin, pinlen);
+ err = iso7816_verify (app_get_slot (app), keyref, pin, pinlen);
wipememory (pin, pinlen);
xfree (pin);
if (err)
@@ -1918,7 +1961,8 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
apdu[1] = ISO7816_VERIFY;
apdu[2] = 0xff;
apdu[3] = keyref;
- err = iso7816_apdu_direct (app->slot, apdu, 4, 0, NULL, NULL, NULL);
+ err = iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0,
+ NULL, NULL, NULL);
goto leave;
}
@@ -1940,7 +1984,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
apdu[1] = ISO7816_VERIFY;
apdu[2] = 0x00;
apdu[3] = keyref;
- if (!iso7816_apdu_direct (app->slot, apdu, 4, 0, &sw, NULL, NULL))
+ if (!iso7816_apdu_direct (app_get_slot (app), apdu, 4, 0, &sw, NULL, NULL))
remaining = -1; /* Already verified, thus full number of tries. */
else if ((sw & 0xfff0) == 0x63C0)
remaining = (sw & 0x000f); /* PIN has REMAINING tries left. */
@@ -1957,7 +2001,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
* old is wrong. This is not possible for the PUK, though. */
if (keyref != 0x81)
{
- err = iso7816_verify (app->slot, keyref, oldpin, oldpinlen);
+ err = iso7816_verify (app_get_slot (app), keyref, oldpin, oldpinlen);
if (err)
{
log_error ("CHV %02X verification failed: %s\n",
@@ -1982,7 +2026,8 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
}
memcpy (buf, oldpin, oldpinlen);
memcpy (buf+oldpinlen, newpin, newpinlen);
- err = iso7816_reset_retry_counter_with_rc (app->slot, targetkeyref,
+ err = iso7816_reset_retry_counter_with_rc (app_get_slot (app),
+ targetkeyref,
buf, oldpinlen+newpinlen);
xfree (buf);
if (err)
@@ -1991,7 +2036,7 @@ do_change_chv (app_t app, ctrl_t ctrl, const char *pwidstr,
}
else
{
- err = iso7816_change_reference_data (app->slot, keyref,
+ err = iso7816_change_reference_data (app_get_slot (app), keyref,
oldpin, oldpinlen,
newpin, newpinlen);
if (err)
@@ -2230,7 +2275,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
goto leave;
/* Note: the -1 requests command chaining. */
- err = iso7816_general_authenticate (app->slot, -1,
+ err = iso7816_general_authenticate (app_get_slot (app), -1,
mechanism, keyref,
apdudata, (int)apdudatalen, 0,
&outdata, &outdatalen);
@@ -2395,15 +2440,34 @@ do_decipher (app_t app, const char *keyidstr,
/* Check that the ciphertext has the right length; due to internal
* convey mechanism using MPIs leading zero bytes might have been
- * lost. Adjust for this. Note that for ECC this actually
- * superfluous because the first octet is always '04' to indicate an
+ * lost. Adjust for this. Unfortunately the ciphertext might have
+ * also been prefixed with a leading zero to make it a positive
+ * number; that may be a too long frame and we need to adjust for
+ * this too. Note that for ECC thoses fixes are not reqquired
+ * because the first octet is always '04' to indicate an
* uncompressed point. */
if (indatalen > framelen)
{
- err = gpg_error (GPG_ERR_INV_VALUE);
- log_error ("piv: input of %zu octets too large for mechanism %d\n",
- indatalen, mechanism);
- goto leave;
+ if (mechanism == PIV_ALGORITHM_RSA
+ && indatalen == framelen + 1 && !*indata)
+ {
+ indata_buffer = xtrycalloc (1, framelen);
+ if (!indata_buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (indata_buffer, indata+1, framelen);
+ indata = indata_buffer;
+ indatalen = framelen;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ log_error ("piv: input of %zu octets too large for mechanism %d\n",
+ indatalen, mechanism);
+ goto leave;
+ }
}
if (indatalen < framelen)
{
@@ -2434,7 +2498,7 @@ do_decipher (app_t app, const char *keyidstr,
goto leave;
/* Note: the -1 requests command chaining. */
- err = iso7816_general_authenticate (app->slot, -1,
+ err = iso7816_general_authenticate (app_get_slot (app), -1,
mechanism, keyref,
apdudata, (int)apdudatalen, 0,
&outdata, &outdatalen);
@@ -2635,7 +2699,7 @@ writekey_rsa (app_t app, data_object_t dobj, int keyref,
if (err)
goto leave;
- err = iso7816_send_apdu (app->slot,
+ err = iso7816_send_apdu (app_get_slot (app),
-1, /* Use command chaining. */
0, /* Class */
0xfe, /* Ins: Yubikey Import Asym. Key. */
@@ -2657,7 +2721,7 @@ writekey_rsa (app_t app, data_object_t dobj, int keyref,
if (err)
goto leave;
tmpl[0] = PIV_ALGORITHM_RSA;
- err = put_data (app->slot, dobj->tag,
+ err = put_data (app_get_slot (app), dobj->tag,
(int)0x80, (size_t)1, tmpl,
(int)0x7f49, (size_t)apdudatalen, apdudata,
(int)0, (size_t)0, NULL);
@@ -2787,7 +2851,7 @@ writekey_ecc (app_t app, data_object_t dobj, int keyref,
if (err)
goto leave;
- err = iso7816_send_apdu (app->slot,
+ err = iso7816_send_apdu (app_get_slot (app),
-1, /* Use command chaining. */
0, /* Class */
0xfe, /* Ins: Yubikey Import Asym. Key. */
@@ -2808,7 +2872,7 @@ writekey_ecc (app_t app, data_object_t dobj, int keyref,
if (err)
goto leave;
tmpl[0] = mechanism;
- err = put_data (app->slot, dobj->tag,
+ err = put_data (app_get_slot (app), dobj->tag,
(int)0x80, (size_t)1, tmpl,
(int)0x7f49, (size_t)apdudatalen, apdudata,
(int)0, (size_t)0, NULL);
@@ -2894,7 +2958,7 @@ do_writekey (app_t app, ctrl_t ctrl,
/* First clear an existing key. We do this by writing an empty 7f49
* tag. This will return GPG_ERR_NO_PUBKEY on a later read. */
flush_cached_data (app, dobj->tag);
- err = put_data (app->slot, dobj->tag,
+ err = put_data (app_get_slot (app), dobj->tag,
(int)0x7f49, (size_t)0, "",
(int)0, (size_t)0, NULL);
if (err)
@@ -3122,7 +3186,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype,
tmpl[3] = 1;
tmpl[4] = mechanism;
tmpllen = 5;
- err = iso7816_generate_keypair (app->slot, 0, 0, keyref,
+ err = iso7816_generate_keypair (app_get_slot (app), 0, 0, keyref,
tmpl, tmpllen, 0, &buffer, &buflen);
if (err)
{
@@ -3152,7 +3216,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keyrefstr, const char *keytype,
tmpl[0] = mechanism;
flush_cached_data (app, dobj->tag);
- err = put_data (app->slot, dobj->tag,
+ err = put_data (app_get_slot (app), dobj->tag,
(int)0x80, (size_t)1, tmpl,
(int)0x7f49, (size_t)keydatalen, keydata,
(int)0, (size_t)0, NULL);
@@ -3205,7 +3269,7 @@ do_writecert (app_t app, ctrl_t ctrl,
* GPG_ERR_NO_PUBKEY). We enforce this because otherwise the only
* way to detect whether a key exists is by trying to use that
* key. */
- err = do_readkey (app, certrefstr, &orig_pk, &orig_pklen);
+ err = do_readkey (app, ctrl, certrefstr, 0, &orig_pk, &orig_pklen);
if (err)
{
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@@ -3223,7 +3287,7 @@ do_writecert (app_t app, ctrl_t ctrl,
goto leave;
}
- err = put_data (app->slot, dobj->tag,
+ err = put_data (app_get_slot (app), dobj->tag,
(int)0x70, (size_t)certlen, cert,/* Certificate */
(int)0x71, (size_t)1, "", /* No compress */
(int)0xfe, (size_t)0, "", /* Empty LRC. */
@@ -3242,14 +3306,121 @@ do_writecert (app_t app, ctrl_t ctrl,
}
+/* 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)
+{
+ gpg_error_t err;
+ char *keygripstr = NULL;
+ char *serialno = NULL;
+ char idbuf[20];
+ int data = 0;
+ int i, tag, dummy_got_cert;
+
+ /* 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 (i = 0; (tag = data_objects[i].tag); i++)
+ {
+ if (!data_objects[i].keypair)
+ continue;
+
+ xfree (keygripstr);
+ if (get_keygrip_by_tag (app, tag, &keygripstr, &dummy_got_cert))
+ continue;
+
+ if (action == KEYGRIP_ACTION_LOOKUP)
+ {
+ if (!strcmp (keygripstr, want_keygripstr))
+ {
+ err = 0; /* Found */
+ goto leave;
+ }
+ }
+ else if (!want_keygripstr || !strcmp (keygripstr, want_keygripstr))
+ {
+ snprintf (idbuf, sizeof idbuf, "PIV.%s", data_objects[i].keyref);
+ 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 (keygripstr);
+ xfree (serialno);
+ return err;
+}
+
+
+/* Reselect the application. This is used by cards which support
+ * on-the-fly switching between applications. */
+static gpg_error_t
+do_reselect (app_t app, ctrl_t ctrl)
+{
+ gpg_error_t err;
+
+ (void)ctrl;
+
+ /* An extra check which should not be necessary because the caller
+ * should have made sure that a re-select is only called for
+ * approriate cards. */
+ if (!app->app_local->flags.yubikey)
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ err = iso7816_select_application (app_get_slot (app),
+ piv_aid, sizeof piv_aid, 0x0001);
+ return err;
+}
+
+
/* Select the PIV application on the card in SLOT. This function must
* be used before any other PIV application functions. */
gpg_error_t
app_select_piv (app_t app)
{
- static char const aid[] = { 0xA0, 0x00, 0x00, 0x03, 0x08, /* RID=NIST */
- 0x00, 0x00, 0x10, 0x00 /* PIX=PIV */ };
- int slot = app->slot;
+ int slot = app_get_slot (app);
gpg_error_t err;
unsigned char *apt = NULL;
size_t aptlen;
@@ -3259,12 +3430,12 @@ app_select_piv (app_t app)
/* Note that we select using the AID without the 2 octet version
* number. This allows for better reporting of future specs. We
* need to use the use-zero-for-P2-flag. */
- err = iso7816_select_application_ext (slot, aid, sizeof aid, 0x0001,
+ err = iso7816_select_application_ext (slot, piv_aid, sizeof piv_aid, 0x0001,
&apt, &aptlen);
if (err)
goto leave;
- app->apptype = "PIV";
+ app->apptype = APPTYPE_PIV;
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
@@ -3279,7 +3450,7 @@ app_select_piv (app_t app)
}
s = find_tlv (apt, aptlen, 0x4F, &n);
- if (!s || n != 6 || memcmp (s, aid+5, 4))
+ if (!s || n != 6 || memcmp (s, piv_aid+5, 4))
{
/* The PIX does not match. */
log_error ("piv: missing or invalid DO 0x4F in APT\n");
@@ -3302,7 +3473,7 @@ app_select_piv (app_t app)
goto leave;
}
s = find_tlv (s, n, 0x4F, &n);
- if (!s || n != 5 || memcmp (s, aid, 5))
+ if (!s || n != 5 || memcmp (s, piv_aid, 5))
{
/* The RID does not match. */
log_error ("piv: missing or invalid DO 0x79.4F in APT\n");
@@ -3317,7 +3488,7 @@ app_select_piv (app_t app)
goto leave;
}
- if (app->cardtype && !strcmp (app->cardtype, "yubikey"))
+ if (app->card->cardtype == CARDTYPE_YUBIKEY)
app->app_local->flags.yubikey = 1;
@@ -3327,6 +3498,7 @@ app_select_piv (app_t app)
dump_all_do (slot);
app->fnc.deinit = do_deinit;
+ app->fnc.reselect = do_reselect;
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert;
app->fnc.readkey = do_readkey;
@@ -3340,6 +3512,7 @@ app_select_piv (app_t app)
app->fnc.decipher = do_decipher;
app->fnc.change_pin = do_change_chv;
app->fnc.check_pin = do_check_chv;
+ app->fnc.with_keygrip = do_with_keygrip;
leave: