diff --git a/tools/Makefile.am b/tools/Makefile.am
index ad0f223b4..69f4098ca 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -129,6 +129,7 @@ gpg_card_tool_SOURCES = \
card-tool.h \
card-call-scd.c \
card-tool-keys.c \
+ card-tool-yubikey.c \
card-tool-misc.c
gpg_card_tool_LDADD = ../common/libgpgrl.a $(common_libs) \
diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c
index 97fb6d9f1..8610da8b6 100644
--- a/tools/card-call-scd.c
+++ b/tools/card-call-scd.c
@@ -445,12 +445,20 @@ store_serialno (const char *line)
/* Send an APDU to the current card. On success the status word is
* stored at R_SW inless R_SW is NULL. With HEXAPDU being NULL only a
* RESET command is send to scd. With HEXAPDU being the string
- * "undefined" the command "SERIALNO undefined" is send to scd. */
+ * "undefined" the command "SERIALNO undefined" is send to scd. If
+ * R_DATA is not NULL the data is without the status code is stored
+ * there. Caller must release it. */
gpg_error_t
-scd_apdu (const char *hexapdu, unsigned int *r_sw)
+scd_apdu (const char *hexapdu, unsigned int *r_sw,
+ unsigned char **r_data, size_t *r_datalen)
{
gpg_error_t err;
+ if (r_data)
+ *r_data = NULL;
+ if (r_datalen)
+ *r_datalen = 0;
+
err = start_agent (START_AGENT_NO_STARTUP_CMDS);
if (err)
return err;
@@ -489,6 +497,12 @@ scd_apdu (const char *hexapdu, unsigned int *r_sw)
{
if (r_sw)
*r_sw = buf16_to_uint (data+datalen-2);
+ if (r_data && r_datalen)
+ {
+ *r_data = data;
+ *r_datalen = datalen - 2;
+ data = NULL;
+ }
}
xfree (data);
}
diff --git a/tools/card-tool-misc.c b/tools/card-tool-misc.c
index 06fcb6705..d0fb55dab 100644
--- a/tools/card-tool-misc.c
+++ b/tools/card-tool-misc.c
@@ -77,3 +77,37 @@ hex_to_buffer (const char *string, size_t *r_length)
*r_length = n;
return buffer;
}
+
+
+/* Direct sending of an hex encoded APDU with error printing. This is
+ * a simple wrapper around scd_apdu. */
+gpg_error_t
+send_apdu (const char *hexapdu, const char *desc, unsigned int ignore,
+ unsigned char **r_data, size_t *r_datalen)
+{
+ gpg_error_t err;
+ unsigned int sw;
+
+ err = scd_apdu (hexapdu, &sw, r_data, r_datalen);
+ if (err)
+ log_error ("sending card command %s failed: %s\n", desc,
+ gpg_strerror (err));
+ else if (!hexapdu || !strcmp (hexapdu, "undefined"))
+ ;
+ else if (ignore == 0xffff)
+ ; /* Ignore all status words. */
+ else if (sw != 0x9000)
+ {
+ switch (sw)
+ {
+ case 0x6285: err = gpg_error (GPG_ERR_OBJ_TERM_STATE); break;
+ case 0x6982: err = gpg_error (GPG_ERR_BAD_PIN); break;
+ case 0x6985: err = gpg_error (GPG_ERR_USE_CONDITIONS); break;
+ default: err = gpg_error (GPG_ERR_CARD);
+ }
+ if (!(ignore && ignore == sw))
+ log_error ("card command %s failed: %s (0x%04x)\n", desc,
+ gpg_strerror (err), sw);
+ }
+ return err;
+}
diff --git a/tools/card-tool-yubikey.c b/tools/card-tool-yubikey.c
new file mode 100644
index 000000000..a03915abf
--- /dev/null
+++ b/tools/card-tool-yubikey.c
@@ -0,0 +1,438 @@
+/* card-tool-yubikey.c - Yubikey specific functions.
+ * Copyright (C) 2019 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../common/util.h"
+#include "../common/i18n.h"
+#include "../common/tlv.h"
+#include "../common/ttyio.h"
+#include "card-tool.h"
+
+
+/* Object to describe requested interface options. */
+struct iface_s {
+ unsigned int usb:1;
+ unsigned int nfc:1;
+};
+
+
+/* Bit flags as used by the fields in struct ykapps_s. */
+#define YKAPP_USB_SUPPORTED 0x01
+#define YKAPP_USB_ENABLED 0x02
+#define YKAPP_NFC_SUPPORTED 0x04
+#define YKAPP_NFC_ENABLED 0x08
+#define YKAPP_SELECTED 0x80 /* Selected by the command. */
+
+/* An object to describe the applications on a Yubikey. Each field
+ * has 8 bits to hold the above flag values. */
+struct ykapps_s {
+ unsigned int otp:8;
+ unsigned int u2f:8;
+ unsigned int opgp:8;
+ unsigned int piv:8;
+ unsigned int oath:8;
+ unsigned int fido2:8;
+};
+
+
+
+/* Helper to parse an unsigned integer config value consisting of bit
+ * flags. TAG select the config item and MASK is the mask ORed into
+ * the value for a set bit. The function modifies YK. */
+static gpg_error_t
+parse_ul_config_value (struct ykapps_s *yk,
+ const unsigned char *config, size_t configlen,
+ int tag, unsigned int mask)
+{
+ const unsigned char *s;
+ size_t n;
+ unsigned long ul = 0;
+ int i;
+
+ s = find_tlv (config, configlen, tag, &n);
+ if (s && n)
+ {
+ if (n > sizeof ul)
+ {
+ log_error ("too large integer in Yubikey config tag %02x detected\n",
+ tag);
+ if (opt.verbose)
+ log_printhex (config, configlen, "config:");
+ return gpg_error (GPG_ERR_CARD);
+ }
+ for (i=0; i < n; i++)
+ {
+ ul <<=8;
+ ul |= s[i];
+ }
+ if (ul & 0x01)
+ yk->otp |= mask;
+ if (ul & 0x02)
+ yk->u2f |= mask;
+ if (ul & 0x08)
+ yk->opgp |= mask;
+ if (ul & 0x10)
+ yk->piv |= mask;
+ if (ul & 0x20)
+ yk->oath |= mask;
+ if (ul & 0x200)
+ yk->fido2 |= mask;
+ }
+ return 0;
+}
+
+
+/* Create an unsigned integer config value for TAG from the data in YK
+ * and store it the provided 4 byte buffer RESULT. If ENABLE is true
+ * the respective APP_SELECTED bit in YK sets the corresponding bit
+ * flags, it is is false that bit flag is cleared. IF APP_SELECTED is
+ * not set the bit flag is not changed. */
+static void
+set_ul_config_value (struct ykapps_s *yk,
+ unsigned int bitflag, int tag, unsigned int enable,
+ unsigned char *result)
+{
+ unsigned long ul = 0;
+
+ /* First set the current values. */
+ if ((yk->otp & bitflag))
+ ul |= 0x01;
+ if ((yk->u2f & bitflag))
+ ul |= 0x02;
+ if ((yk->opgp & bitflag))
+ ul |= 0x08;
+ if ((yk->piv & bitflag))
+ ul |= 0x10;
+ if ((yk->oath & bitflag))
+ ul |= 0x20;
+ if ((yk->fido2 & bitflag))
+ ul |= 0x200;
+
+ /* Then enable or disable the bits according to the selection flag. */
+ if (enable)
+ {
+ if ((yk->otp & YKAPP_SELECTED))
+ ul |= 0x01;
+ if ((yk->u2f & YKAPP_SELECTED))
+ ul |= 0x02;
+ if ((yk->opgp & YKAPP_SELECTED))
+ ul |= 0x08;
+ if ((yk->piv & YKAPP_SELECTED))
+ ul |= 0x10;
+ if ((yk->oath & YKAPP_SELECTED))
+ ul |= 0x20;
+ if ((yk->fido2 & YKAPP_SELECTED))
+ ul |= 0x200;
+ }
+ else
+ {
+ if ((yk->otp & YKAPP_SELECTED))
+ ul &= ~0x01;
+ if ((yk->u2f & YKAPP_SELECTED))
+ ul &= ~0x02;
+ if ((yk->opgp & YKAPP_SELECTED))
+ ul &= ~0x08;
+ if ((yk->piv & YKAPP_SELECTED))
+ ul &= ~0x10;
+ if ((yk->oath & YKAPP_SELECTED))
+ ul &= ~0x20;
+ if ((yk->fido2 & YKAPP_SELECTED))
+ ul &= ~0x200;
+ }
+
+ /* Make sure that we do not disable the CCID transport. Without
+ * CCID we won't have any way to change the configuration again. We
+ * would instead need one of the other Yubikey tools to enable an
+ * application and thus its transport again. */
+ if (bitflag == YKAPP_USB_ENABLED && !(ul & (0x08|0x10|0x20)))
+ {
+ log_info ("Enabling PIV so that at least one CCI transport is enabled\n");
+ ul |= 0x10;
+ }
+
+ result[0] = tag;
+ result[1] = 2;
+ result[2] = ul >> 8;
+ result[3] = ul;
+}
+
+
+/* Print the info from YK. */
+static void
+yk_list (estream_t fp, struct ykapps_s *yk)
+{
+ if (opt.interactive)
+ tty_fprintf (fp, ("Application USB NFC\n"
+ "-----------------------\n"));
+ tty_fprintf (fp, "OTP %s %s\n",
+ (yk->otp & YKAPP_USB_SUPPORTED)?
+ (yk->otp & YKAPP_USB_ENABLED? "yes" : "no ") : "- ",
+ (yk->otp & YKAPP_NFC_SUPPORTED)?
+ (yk->otp & YKAPP_NFC_ENABLED? "yes" : "no ") : "- ");
+ tty_fprintf (fp, "U2F %s %s\n",
+ (yk->otp & YKAPP_USB_SUPPORTED)?
+ (yk->otp & YKAPP_USB_ENABLED? "yes" : "no ") : "- ",
+ (yk->otp & YKAPP_NFC_SUPPORTED)?
+ (yk->otp & YKAPP_NFC_ENABLED? "yes" : "no ") : "- ");
+ tty_fprintf (fp, "OPGP %s %s\n",
+ (yk->opgp & YKAPP_USB_SUPPORTED)?
+ (yk->opgp & YKAPP_USB_ENABLED? "yes" : "no ") : "- ",
+ (yk->opgp & YKAPP_NFC_SUPPORTED)?
+ (yk->opgp & YKAPP_NFC_ENABLED? "yes" : "no ") : "- ");
+ tty_fprintf (fp, "PIV %s %s\n",
+ (yk->piv & YKAPP_USB_SUPPORTED)?
+ (yk->piv & YKAPP_USB_ENABLED? "yes" : "no ") : "- ",
+ (yk->piv & YKAPP_NFC_SUPPORTED)?
+ (yk->piv & YKAPP_NFC_ENABLED? "yes" : "no ") : "- ");
+ tty_fprintf (fp, "OATH %s %s\n",
+ (yk->oath & YKAPP_USB_SUPPORTED)?
+ (yk->oath & YKAPP_USB_ENABLED? "yes" : "no ") : "- ",
+ (yk->oath & YKAPP_NFC_SUPPORTED)?
+ (yk->oath & YKAPP_NFC_ENABLED? "yes" : "no ") : "- ");
+ tty_fprintf (fp, "FIDO2 %s %s\n",
+ (yk->fido2 & YKAPP_USB_SUPPORTED)?
+ (yk->fido2 & YKAPP_USB_ENABLED? "yes" : "no ") : "- ",
+ (yk->fido2 & YKAPP_NFC_SUPPORTED)?
+ (yk->fido2 & YKAPP_NFC_ENABLED? "yes" : "no ") : "- ");
+}
+
+
+/* Enable disable the apps as marked in YK with flag YKAPP_SELECTED. */
+static gpg_error_t
+yk_enable_disable (struct ykapps_s *yk, struct iface_s *iface,
+ const unsigned char *config, size_t configlen, int enable)
+{
+ gpg_error_t err = 0;
+ unsigned char apdu[100];
+ unsigned int apdulen;
+ /* const unsigned char *s; */
+ /* size_t n; */
+ char *hexapdu = NULL;
+
+ apdulen = 0;
+ apdu[apdulen++] = 0x00;
+ apdu[apdulen++] = 0x1c; /* Write Config instruction. */
+ apdu[apdulen++] = 0x00;
+ apdu[apdulen++] = 0x00;
+ apdu[apdulen++] = 0x00; /* Lc will be fixed up later. */
+ apdu[apdulen++] = 0x00; /* Length of data will also be fixed up later. */
+
+ /* The ykman tool has no way to set NFC and USB flags in one go.
+ * Reasoning about the Yubikey's firmware it seems plausible that
+ * combining should work. Let's try it here if the user called for
+ * setting both interfaces. */
+ if (iface->nfc)
+ {
+ set_ul_config_value (yk, YKAPP_NFC_ENABLED, 0x0e, enable, apdu+apdulen);
+ apdulen += 4;
+ }
+ if (iface->usb)
+ {
+ set_ul_config_value (yk, YKAPP_USB_ENABLED, 0x03, enable, apdu+apdulen);
+ apdulen += 4;
+ /* Yubikey's ykman copies parts of the config data when writing
+ * the config for USB. Below is a commented example on how that
+ * can be done. */
+ (void)config;
+ (void)configlen;
+ /* Copy the device flags. */
+ /* s = find_tlv (config, configlen, 0x08, &n); */
+ /* if (s && n) */
+ /* { */
+ /* s -= 2; */
+ /* n += 2; */
+ /* if (apdulen + n > sizeof apdu) */
+ /* { */
+ /* err = gpg_error (GPG_ERR_BUFFER_TOO_SHORT); */
+ /* goto leave; */
+ /* } */
+ /* memcpy (apdu+apdulen, s, n); */
+ /* apdulen += n; */
+ /* } */
+ }
+ if (iface->nfc || iface->usb)
+ {
+ if (apdulen + 2 > sizeof apdu)
+ {
+ err = gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
+ goto leave;
+ }
+ /* Disable the next two lines to let the card reboot. Not doing
+ * this is however more convenient for this tool because further
+ * commands don't end up with an error. It seems to be better
+ * that a "reset" command from gpg-card-tool is run at the
+ * user's discretion. */
+ /* apdu[apdulen++] = 0x0c; /\* Reboot tag *\/ */
+ /* apdu[apdulen++] = 0; /\* No data for reboot. *\/ */
+ /* Fixup the lngth bytes. */
+ apdu[4] = apdulen - 6 + 1;
+ apdu[5] = apdulen - 6;
+
+ hexapdu = bin2hex (apdu, apdulen, NULL);
+ if (!hexapdu)
+ err = gpg_error_from_syserror ();
+ else
+ err = send_apdu (hexapdu, "YK.write_config", 0, NULL, NULL);
+ }
+
+ leave:
+ xfree (hexapdu);
+ return err;
+}
+
+
+/* Implementation part of cmd_yubikey. ARGV is an array of size ARGc
+ * with the argumets given to the yubikey command. Note that ARGV has
+ * no terminating NULL so that ARGC must be considred. FP is the
+ * stream to output information. This function must only be called on
+ * Yubikeys. */
+gpg_error_t
+yubikey_commands (estream_t fp, int argc, char *argv[])
+{
+ gpg_error_t err;
+ enum {ykLIST, ykENABLE, ykDISABLE } cmd;
+ struct iface_s iface = {0,0};
+ struct ykapps_s ykapps = {0};
+ unsigned char *config = NULL;
+ size_t configlen;
+ int i;
+
+ if (!argc)
+ return gpg_error (GPG_ERR_SYNTAX);
+
+ /* Parse command. */
+ if (!ascii_strcasecmp (argv[0], "list"))
+ cmd = ykLIST;
+ else if (!ascii_strcasecmp (argv[0], "enable"))
+ cmd = ykENABLE;
+ else if (!ascii_strcasecmp (argv[0], "disable"))
+ cmd = ykDISABLE;
+ else
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_COMMAND);
+ goto leave;
+ }
+
+ /* Parse interface if needed. */
+ if (cmd == ykLIST)
+ iface.usb = iface.nfc = 1;
+ else if (argc < 2)
+ {
+ err = gpg_error (GPG_ERR_SYNTAX);
+ goto leave;
+ }
+ else if (!ascii_strcasecmp (argv[1], "usb"))
+ iface.usb = 1;
+ else if (!ascii_strcasecmp (argv[1], "nfc"))
+ iface.nfc = 1;
+ else if (!ascii_strcasecmp (argv[1], "all") || !strcmp (argv[1], "*"))
+ iface.usb = iface.nfc = 1;
+ else
+ {
+ err = gpg_error (GPG_ERR_SYNTAX);
+ goto leave;
+ }
+
+ /* Parse list of applications. */
+ for (i=2; i < argc; i++)
+ {
+ if (!ascii_strcasecmp (argv[i], "otp"))
+ ykapps.otp = 0x80;
+ else if (!ascii_strcasecmp (argv[i], "u2f"))
+ ykapps.u2f = 0x80;
+ else if (!ascii_strcasecmp (argv[i], "opgp")
+ ||!ascii_strcasecmp (argv[i], "openpgp"))
+ ykapps.opgp = 0x80;
+ else if (!ascii_strcasecmp (argv[i], "piv"))
+ ykapps.piv = 0x80;
+ else if (!ascii_strcasecmp (argv[i], "oath")
+ || !ascii_strcasecmp (argv[i], "oauth"))
+ ykapps.oath = 0x80;
+ else if (!ascii_strcasecmp (argv[i], "fido2"))
+ ykapps.fido2 = 0x80;
+ else if (!ascii_strcasecmp (argv[i], "all")|| !strcmp (argv[i], "*"))
+ {
+ ykapps.otp = ykapps.u2f = ykapps.opgp = ykapps.piv = ykapps.oath
+ = ykapps.fido2 = 0x80;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_SYNTAX);
+ goto leave;
+ }
+ }
+
+ /* Select the Yubikey Manager application. */
+ err = send_apdu ("00A4040008a000000527471117", "Select.YK-Manager", 0,
+ NULL, NULL);
+ if (err)
+ goto leave;
+ /* Send the read config command. */
+ err = send_apdu ("001D000000", "YK.read_config", 0, &config, &configlen);
+ if (err)
+ goto leave;
+ if (!configlen || *config > configlen - 1)
+ {
+ /* The length byte is shorter than the actual length. */
+ log_error ("Yubikey returned improper config data\n");
+ log_printhex (config, configlen, "config:");
+ err = gpg_error (GPG_ERR_CARD);
+ goto leave;
+ }
+ if (configlen-1 > *config)
+ {
+ log_info ("Extra config data ignored\n");
+ log_printhex (config, configlen, "config:");
+ }
+ configlen = *config;
+
+ err = parse_ul_config_value (&ykapps, config+1, configlen,
+ 0x01, YKAPP_USB_SUPPORTED);
+ if (!err)
+ err = parse_ul_config_value (&ykapps, config+1, configlen,
+ 0x03, YKAPP_USB_ENABLED);
+ if (!err)
+ err = parse_ul_config_value (&ykapps, config+1, configlen,
+ 0x0d, YKAPP_NFC_SUPPORTED);
+ if (!err)
+ err = parse_ul_config_value (&ykapps, config+1, configlen,
+ 0x0e, YKAPP_NFC_ENABLED);
+ if (err)
+ goto leave;
+
+ switch (cmd)
+ {
+ case ykLIST: yk_list (fp, &ykapps); break;
+ case ykENABLE: err = yk_enable_disable (&ykapps, &iface,
+ config+1, configlen, 1); break;
+ case ykDISABLE: err = yk_enable_disable (&ykapps, &iface,
+ config+1, configlen, 0); break;
+ }
+
+ leave:
+ xfree (config);
+ return err;
+}
diff --git a/tools/card-tool.h b/tools/card-tool.h
index f49f253ce..f83ebf983 100644
--- a/tools/card-tool.h
+++ b/tools/card-tool.h
@@ -192,12 +192,16 @@ gpg_error_t test_get_matching_keys (const char *hexgrip);
/*-- card-tool-misc.c --*/
key_info_t find_kinfo (card_info_t info, const char *keyref);
void *hex_to_buffer (const char *string, size_t *r_length);
+gpg_error_t send_apdu (const char *hexapdu, const char *desc,
+ unsigned int ignore,
+ unsigned char **r_data, size_t *r_datalen);
/*-- card-call-scd.c --*/
void release_card_info (card_info_t info);
const char *app_type_string (app_type_t app_type);
-gpg_error_t scd_apdu (const char *hexapdu, unsigned int *r_sw);
+gpg_error_t scd_apdu (const char *hexapdu, unsigned int *r_sw,
+ unsigned char **r_data, size_t *r_datalen);
gpg_error_t scd_learn (card_info_t info);
gpg_error_t scd_getattr (const char *name, struct card_info_s *info);
gpg_error_t scd_setattr (const char *name,
@@ -218,6 +222,8 @@ gpg_error_t scd_checkpin (const char *serialno);
unsigned long agent_get_s2k_count (void);
+/*-- card-tool-yubikey.c --*/
+gpg_error_t yubikey_commands (estream_t fp, int argc, char *argv[]);
#endif /*GNUPG_CARD_TOOL_H*/
diff --git a/tools/gpg-card-tool.c b/tools/gpg-card-tool.c
index 93153b12f..2bc2e5f90 100644
--- a/tools/gpg-card-tool.c
+++ b/tools/gpg-card-tool.c
@@ -1596,6 +1596,16 @@ cmd_writecert (card_info_t info, char *argstr)
}
certref = certref_buffer = xstrdup ("OPENPGP.3");
}
+ else /* Upcase the certref; prepend cardtype if needed. */
+ {
+ if (!strchr (certref, '.'))
+ certref_buffer = xstrconcat (app_type_string (info->apptype), ".",
+ certref, NULL);
+ else
+ certref_buffer = xstrdup (certref);
+ ascii_strupr (certref_buffer);
+ certref = certref_buffer;
+ }
if (opt_clear)
{
@@ -2156,38 +2166,6 @@ cmd_unblock (card_info_t info)
}
-/* Direct sending of an hex encoded APDU with error printing. */
-static gpg_error_t
-send_apdu (const char *hexapdu, const char *desc, unsigned int ignore)
-{
- gpg_error_t err;
- unsigned int sw;
-
- err = scd_apdu (hexapdu, &sw);
- if (err)
- log_error ("sending card command %s failed: %s\n", desc,
- gpg_strerror (err));
- else if (!hexapdu || !strcmp (hexapdu, "undefined"))
- ;
- else if (ignore == 0xffff)
- ; /* Ignore all status words. */
- else if (sw != 0x9000)
- {
- switch (sw)
- {
- case 0x6285: err = gpg_error (GPG_ERR_OBJ_TERM_STATE); break;
- case 0x6982: err = gpg_error (GPG_ERR_BAD_PIN); break;
- case 0x6985: err = gpg_error (GPG_ERR_USE_CONDITIONS); break;
- default: err = gpg_error (GPG_ERR_CARD);
- }
- if (!(ignore && ignore == sw))
- log_error ("card command %s failed: %s (0x%04x)\n", desc,
- gpg_strerror (err), sw);
- }
- return err;
-}
-
-
/* Note: On successful execution a redisplay should be scheduled. If
* this function fails the card may be in an unknown state. */
static gpg_error_t
@@ -2308,11 +2286,12 @@ cmd_factoryreset (card_info_t info)
* unblock PIN command. */
any_apdu = 1;
for (i=0; i < 5; i++)
- send_apdu ("0020008008FFFFFFFFFFFFFFFF", "VERIFY", 0xffff);
+ send_apdu ("0020008008FFFFFFFFFFFFFFFF", "VERIFY", 0xffff,
+ NULL, NULL);
for (i=0; i < 5; i++)
send_apdu ("002C008010FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
- "RESET RETRY COUNTER", 0xffff);
- err = send_apdu ("00FB000001FF", "YUBIKEY RESET", 0);
+ "RESET RETRY COUNTER", 0xffff, NULL, NULL);
+ err = send_apdu ("00FB000001FF", "YUBIKEY RESET", 0, NULL, NULL);
if (err)
goto leave;
}
@@ -2321,14 +2300,15 @@ cmd_factoryreset (card_info_t info)
any_apdu = 1;
/* We need to select a card application before we can send APDUs
* to the card without scdaemon doing anything on its own. */
- err = send_apdu (NULL, "RESET", 0);
+ err = send_apdu (NULL, "RESET", 0, NULL, NULL);
if (err)
goto leave;
- err = send_apdu ("undefined", "dummy select ", 0);
+ err = send_apdu ("undefined", "dummy select ", 0, NULL, NULL);
if (err)
goto leave;
/* Select the OpenPGP application. */
- err = send_apdu ("00A4040006D27600012401", "SELECT AID", 0);
+ err = send_apdu ("00A4040006D27600012401", "SELECT AID", 0,
+ NULL, NULL);
if (err)
goto leave;
@@ -2343,14 +2323,16 @@ cmd_factoryreset (card_info_t info)
for (i=0; i < 4; i++)
send_apdu ("0020008120"
"40404040404040404040404040404040"
- "40404040404040404040404040404040", "VERIFY", 0xffff);
+ "40404040404040404040404040404040", "VERIFY", 0xffff,
+ NULL, NULL);
for (i=0; i < 4; i++)
send_apdu ("0020008320"
"40404040404040404040404040404040"
- "40404040404040404040404040404040", "VERIFY", 0xffff);
+ "40404040404040404040404040404040", "VERIFY", 0xffff,
+ NULL, NULL);
/* Send terminate datafile command. */
- err = send_apdu ("00e60000", "TERMINATE DF", 0x6985);
+ err = send_apdu ("00e60000", "TERMINATE DF", 0x6985, NULL, NULL);
if (err)
goto leave;
}
@@ -2361,13 +2343,13 @@ cmd_factoryreset (card_info_t info)
any_apdu = 1;
/* Send activate datafile command. This is used without
* confirmation if the card is already in termination state. */
- err = send_apdu ("00440000", "ACTIVATE DF", 0);
+ err = send_apdu ("00440000", "ACTIVATE DF", 0, NULL, NULL);
if (err)
goto leave;
}
/* Finally we reset the card reader once more. */
- err = send_apdu (NULL, "RESET", 0);
+ err = send_apdu (NULL, "RESET", 0, NULL, NULL);
if (err)
goto leave;
@@ -2859,7 +2841,60 @@ cmd_uif (card_info_t info, char *argstr)
}
+static gpg_error_t
+cmd_yubikey (card_info_t info, char *argstr)
+{
+ gpg_error_t err, err2;
+ estream_t fp = opt.interactive? NULL : es_stdout;
+ char *words[20];
+ int nwords;
+
+ if (!info)
+ return print_help
+ ("YUBIKEY args\n\n"
+ "Various commands pertaining to Yubikey tokens with being:\n"
+ "\n"
+ " LIST \n"
+ "\n"
+ "List supported and enabled applications.\n"
+ "\n"
+ " ENABLE usb|nfc|all [otp|u2f|opgp|piv|oath|fido2|all]\n"
+ " DISABLE usb|nfc|all [otp|u2f|opgp|piv|oath|fido2|all]\n"
+ "\n"
+ "Enable or disable the specified or all applications on the\n"
+ "given interface.",
+ 0);
+
+ argstr = skip_options (argstr);
+
+ if (!info->cardtype || strcmp (info->cardtype, "yubikey"))
+ {
+ log_info ("This command can only be used with Yubikeys.\n");
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+
+ nwords = split_fields (argstr, words, DIM (words));
+ if (nwords < 1)
+ {
+ err = gpg_error (GPG_ERR_SYNTAX);
+ goto leave;
+ }
+
+
+ /* Note that we always do a learn to get a chance to the card back
+ * into a usable state. */
+ err = yubikey_commands (fp, nwords, words);
+ err2 = scd_learn (info);
+ if (err2)
+ log_error ("Error re-reading card: %s\n", gpg_strerror (err));
+
+ leave:
+ return err;
+}
+
+
/* Data used by the command parser. This needs to be outside of the
* function scope to allow readline based command completion. */
enum cmdids
@@ -2869,7 +2904,7 @@ enum cmdids
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP,
- cmdKEYATTR, cmdUIF, cmdAUTHENTICATE,
+ cmdKEYATTR, cmdUIF, cmdAUTHENTICATE, cmdYUBIKEY,
cmdINVCMD
};
@@ -2907,10 +2942,10 @@ static struct
{ "kdf-setup", cmdKDFSETUP, 1, N_("setup KDF for PIN authentication")},
{ "key-attr", cmdKEYATTR, 1, N_("change the key attribute")},
{ "uif", cmdUIF, 1, N_("change the User Interaction Flag")},
- /* Note, that we do not announce these command yet. */
{ "privatedo", cmdPRIVATEDO, 0, N_("change a private data object")},
{ "readcert", cmdREADCERT, 0, N_("read a certificate from a data object")},
{ "writecert", cmdWRITECERT, 1, N_("store a certificate to a data object")},
+ { "yubikey", cmdYUBIKEY, 0, N_("Yubikey management commands")},
{ NULL, cmdINVCMD, 0, NULL }
};
@@ -3020,7 +3055,7 @@ dispatch_command (card_info_t info, const char *orig_command)
else
{
flush_keyblock_cache ();
- err = scd_apdu (NULL, NULL);
+ err = scd_apdu (NULL, NULL, NULL, NULL);
}
break;
@@ -3048,6 +3083,7 @@ dispatch_command (card_info_t info, const char *orig_command)
case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
case cmdKEYATTR: err = cmd_keyattr (info, argstr); break;
case cmdUIF: err = cmd_uif (info, argstr); break;
+ case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
case cmdINVCMD:
default:
@@ -3262,7 +3298,7 @@ interactive_loop (void)
else
{
flush_keyblock_cache ();
- err = scd_apdu (NULL, NULL);
+ err = scd_apdu (NULL, NULL, NULL, NULL);
}
break;
@@ -3318,6 +3354,7 @@ interactive_loop (void)
case cmdKDFSETUP: err = cmd_kdfsetup (info, argstr); break;
case cmdKEYATTR: err = cmd_keyattr (info, argstr); break;
case cmdUIF: err = cmd_uif (info, argstr); break;
+ case cmdYUBIKEY: err = cmd_yubikey (info, argstr); break;
case cmdINVCMD:
default: