Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yubikey 5.3+: Add support for PIN policy and touch policy #3071

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
103 changes: 101 additions & 2 deletions src/libopensc/card-piv.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@
#include "compression.h"
#endif

#include "internal.h"
#include "asn1.h"
#include "cardctl.h"
#include "internal.h"
#include "simpletlv.h"
#include "ui/notify.h"

enum {
PIV_OBJ_CCC = 0,
Expand Down Expand Up @@ -414,6 +415,11 @@ typedef struct piv_private_data {
unsigned int card_issues; /* card_issues flags for this card */
int object_test_verify; /* Can test this object to set verification state of card */
int yubico_version; /* 3 byte version number of NEO or Yubikey4 as integer */
struct {
u8 slot;
u8 policy;
u8 touch;
} yk_pin[26];
unsigned int ccc_flags; /* From CCC indicate if CAC card */
unsigned int pin_policy; /* from discovery */
unsigned int init_flags;
Expand Down Expand Up @@ -4378,6 +4384,78 @@ static int piv_get_pin_preference(sc_card_t *card, int *pin_ref)
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}

static void
piv_yk_metadata_get_policy(sc_context_t *ctx, u8 *buf, size_t buflen, u8 *pin, u8 *touch)
{
size_t policylen;
const u8 *policy = sc_asn1_find_tag(ctx, buf, buflen, 0x02, &policylen);
if (policy && policylen == 2) {
*pin = policy[0];
*touch = policy[1];
} else {
sc_log(ctx, "Yubikey PIN policy not found");
}
}

static void
piv_yk_get_metadata(sc_card_t *card, u8 slot, u8 *pin_policy, u8 *touch_policy)
{
sc_apdu_t apdu;
u8 resp[100];
size_t i;
piv_private_data_t *priv = PIV_DATA(card);

/* initialize with the default behaviour */
if (pin_policy)
*pin_policy = 0x00;
if (touch_policy)
*touch_policy = 0x00;

if (priv->yubico_version < 0x00050300) {
if (priv->yubico_version != 0)
sc_log(card->ctx, "Yubikey's PIN and touch policy not available");
return;
}

for (i = 0; i < (sizeof(priv->yk_pin) / sizeof(*priv->yk_pin) - 1); i++) {
if (priv->yk_pin[i].slot == 0x00)
/* reached the last initialized entry */
break;

if (priv->yk_pin[i].slot == slot)
/* metadata already initialized */
break;
}

if (priv->yk_pin[i].slot == 0x00) {
/* initialize this entry */
sc_format_apdu_ex(&apdu, 0x00, 0xF7, 0x00, slot, NULL, 0, resp, sizeof resp);
if (SC_SUCCESS == sc_transmit_apdu(card, &apdu) && SC_SUCCESS == sc_check_sw(card, apdu.sw1, apdu.sw2)) {
piv_yk_metadata_get_policy(card->ctx, resp, apdu.resplen,
&priv->yk_pin[i].policy,
&priv->yk_pin[i].touch);
sc_log(card->ctx, "PIN policy for slot 0x%02X: 0x%02X (touch 0x%02X)",
slot, priv->yk_pin[i].policy, priv->yk_pin[i].touch);
priv->yk_pin[i].slot = slot;
}
}

if (priv->yk_pin[i].slot == slot) {
if (pin_policy)
*pin_policy = priv->yk_pin[i].policy;
if (touch_policy)
*touch_policy = priv->yk_pin[i].touch;
}
}

static int
piv_yk_pin_policy(sc_card_t *card, u8 *ptr)
{
u8 slot = *ptr;
piv_yk_get_metadata(card, slot, ptr, NULL);
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}

static int piv_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
piv_private_data_t * priv = PIV_DATA(card);
Expand Down Expand Up @@ -4415,6 +4493,9 @@ static int piv_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
case SC_CARDCTL_PIV_OBJECT_PRESENT:
return piv_is_object_present(card, ptr);
break;
case SC_CARDCTL_PIV_YK_PIN_POLICY:
return piv_yk_pin_policy(card, ptr);
break;
}

LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
Expand Down Expand Up @@ -4527,6 +4608,22 @@ static int piv_restore_security_env(sc_card_t *card, int se_num)
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}

static void
piv_yk_notify_touch_policy(sc_card_t *card, u8 key_ref)
{
u8 touch_policy;
const char *title = "Touch your Yubikey to continue";

piv_yk_get_metadata(card, key_ref, NULL, &touch_policy);
switch (touch_policy) {
case 0x02:
sc_notify(title, "Touching the token is required for unlocking the key");
break;
case 0x03:
sc_notify(title, "Touching the token is required unlocking the key (needed again after 15 seconds)");
break;
}
}

static int piv_validate_general_authentication(sc_card_t *card,
const u8 * data, size_t datalen,
Expand Down Expand Up @@ -4589,6 +4686,7 @@ static int piv_validate_general_authentication(sc_card_t *card,
}
/* EC alg_id was already set */

piv_yk_notify_touch_policy(card, priv->key_ref);
r = piv_general_io(card, 0x87, real_alg_id, priv->key_ref,
sbuf, p - sbuf, rbuf, sizeof rbuf);
if (r < 0)
Expand Down Expand Up @@ -4627,6 +4725,7 @@ piv_compute_signature(sc_card_t *card, const u8 * data, size_t datalen,
u8 rbuf[128]; /* For EC conversions 384 will fit */

SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

/* The PIV returns a DER SEQUENCE{INTEGER, INTEGER}
* Which may have leading 00 to force a positive integer
* But PKCS11 just wants 2* field_length in bytes
Expand Down Expand Up @@ -5493,7 +5592,7 @@ static int piv_match_card_continued(sc_card_t *card)
apdu.resplen = sizeof(yubico_version_buf);
apdu.le = apdu.resplen;
r2 = sc_transmit_apdu(card, &apdu); /* on error yubico_version == 0 */
if (r2 >= 3) {
if (r2 == SC_SUCCESS && apdu.resplen == 3) {
priv->yubico_version = (yubico_version_buf[0]<<16) | (yubico_version_buf[1] <<8) | yubico_version_buf[2];
sc_log(card->ctx, "Yubico card->type=%d, r=0x%08x version=0x%08x", card->type, r, priv->yubico_version);
}
Expand Down
61 changes: 31 additions & 30 deletions src/libopensc/cardctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ enum {
/*
* TCOS specific calls
*/
SC_CARDCTL_TCOS_BASE = _CTL_PREFIX('T','C','S'),
SC_CARDCTL_TCOS_BASE = _CTL_PREFIX('T', 'C', 'S'),
SC_CARDCTL_TCOS_SETPERM,

/*
Expand Down Expand Up @@ -99,7 +99,7 @@ enum {
/*
* Muscle specific calls
*/
SC_CARDCTL_MUSCLE_BASE = _CTL_PREFIX('M','S','C'),
SC_CARDCTL_MUSCLE_BASE = _CTL_PREFIX('M', 'S', 'C'),
SC_CARDCTL_MUSCLE_GENERATE_KEY,
SC_CARDCTL_MUSCLE_EXTRACT_KEY,
SC_CARDCTL_MUSCLE_IMPORT_KEY,
Expand All @@ -108,26 +108,26 @@ enum {
/*
* ASEPCOS specific calls
*/
SC_CARDCTL_ASEPCOS_BASE = _CTL_PREFIX('A','S','E'),
SC_CARDCTL_ASEPCOS_BASE = _CTL_PREFIX('A', 'S', 'E'),
SC_CARDCTL_ASEPCOS_CHANGE_KEY,
SC_CARDCTL_ASEPCOS_AKN2FILEID,
SC_CARDCTL_ASEPCOS_SET_SATTR,
SC_CARDCTL_ASEPCOS_ACTIVATE_FILE,

/*
/*
* ruToken specific calls
*/
SC_CARDCTL_RUTOKEN_BASE = _CTL_PREFIX('R', 'T', 'K'),
/* PUT_DATA */
SC_CARDCTL_RUTOKEN_CREATE_DO,
SC_CARDCTL_RUTOKEN_CHANGE_DO,
SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO,
SC_CARDCTL_RUTOKEN_DELETE_DO,
SC_CARDCTL_RUTOKEN_GET_INFO,
/* NON STANDARD */
SC_CARDCTL_RUTOKEN_GET_DO_INFO,
SC_CARDCTL_RUTOKEN_GOST_ENCIPHER,
SC_CARDCTL_RUTOKEN_GOST_DECIPHER,
SC_CARDCTL_RUTOKEN_BASE = _CTL_PREFIX('R', 'T', 'K'),
/* PUT_DATA */
SC_CARDCTL_RUTOKEN_CREATE_DO,
SC_CARDCTL_RUTOKEN_CHANGE_DO,
SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO,
SC_CARDCTL_RUTOKEN_DELETE_DO,
SC_CARDCTL_RUTOKEN_GET_INFO,
/* NON STANDARD */
SC_CARDCTL_RUTOKEN_GET_DO_INFO,
SC_CARDCTL_RUTOKEN_GOST_ENCIPHER,
SC_CARDCTL_RUTOKEN_GOST_DECIPHER,
SC_CARDCTL_RUTOKEN_FORMAT_INIT,
SC_CARDCTL_RUTOKEN_FORMAT_END,

Expand All @@ -150,8 +150,8 @@ enum {
SC_CARDCTL_RTECP_GENERATE_KEY,

/*
* Westcos specific
*/
* Westcos specific
*/
SC_CARDCTL_WESTCOS_FREEZE = _CTL_PREFIX('W', 'T', 'C'),
SC_CARDCTL_WESTCOS_CREATE_MF,
SC_CARDCTL_WESTCOS_COMMIT,
Expand All @@ -178,6 +178,7 @@ enum {
SC_CARDCTL_PIV_GENERATE_KEY,
SC_CARDCTL_PIV_PIN_PREFERENCE,
SC_CARDCTL_PIV_OBJECT_PRESENT,
SC_CARDCTL_PIV_YK_PIN_POLICY,

/*
* CAC specific calls
Expand All @@ -191,14 +192,14 @@ enum {
SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS,
SC_CARDCTL_CAC_GET_ACA_PATH,

/*
/*
* AuthentIC v3
*/
SC_CARDCTL_AUTHENTIC_BASE = _CTL_PREFIX('A','V','3'),
SC_CARDCTL_AUTHENTIC_SDO_CREATE,
SC_CARDCTL_AUTHENTIC_SDO_DELETE,
SC_CARDCTL_AUTHENTIC_SDO_STORE,
SC_CARDCTL_AUTHENTIC_SDO_GENERATE,
SC_CARDCTL_AUTHENTIC_BASE = _CTL_PREFIX('A', 'V', '3'),
SC_CARDCTL_AUTHENTIC_SDO_CREATE,
SC_CARDCTL_AUTHENTIC_SDO_DELETE,
SC_CARDCTL_AUTHENTIC_SDO_STORE,
SC_CARDCTL_AUTHENTIC_SDO_GENERATE,

/*
* Coolkey specific calls
Expand All @@ -211,13 +212,13 @@ enum {
SC_CARDCTL_COOLKEY_GET_TOKEN_INFO,
SC_CARDCTL_COOLKEY_FIND_OBJECT,

/*
/*
* IAS/ECC
*/
SC_CARDCTL_IASECC_BASE = _CTL_PREFIX('E','C','C'),
SC_CARDCTL_IASECC_BASE = _CTL_PREFIX('E', 'C', 'C'),
SC_CARDCTL_IASECC_GET_FREE_KEY_REFERENCE,
SC_CARDCTL_IASECC_SDO_MAGIC = _CTL_PREFIX('S','D','O') | 'M',
SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA = _CTL_PREFIX('S','D','O') | 'P',
SC_CARDCTL_IASECC_SDO_MAGIC = _CTL_PREFIX('S', 'D', 'O') | 'M',
SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA = _CTL_PREFIX('S', 'D', 'O') | 'P',
SC_CARDCTL_IASECC_SDO_PUT_DATA,
SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA,
SC_CARDCTL_IASECC_SDO_GET_DATA,
Expand Down Expand Up @@ -248,21 +249,21 @@ enum {
/*
* DNIe specific calls
*/
SC_CARDCTL_DNIE_BASE = _CTL_PREFIX('D', 'N', 'I'),
SC_CARDCTL_DNIE_BASE = _CTL_PREFIX('D', 'N', 'I'),
SC_CARDCTL_DNIE_GENERATE_KEY,
SC_CARDCTL_DNIE_GET_INFO,

/*
* isoApplet Java Card Applet
*/
SC_CARDCTL_ISOAPPLET_BASE = _CTL_PREFIX('I','S','O'),
SC_CARDCTL_ISOAPPLET_BASE = _CTL_PREFIX('I', 'S', 'O'),
SC_CARDCTL_ISOAPPLET_GENERATE_KEY,
SC_CARDCTL_ISOAPPLET_IMPORT_KEY,

/*
* GIDS cards
*/
SC_CARDCTL_GIDS_BASE = _CTL_PREFIX('G','I','D'),
SC_CARDCTL_GIDS_BASE = _CTL_PREFIX('G', 'I', 'D'),
SC_CARDCTL_GIDS_GET_ALL_CONTAINERS,
SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL,
SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE,
Expand Down
16 changes: 15 additions & 1 deletion src/libopensc/pkcs15-piv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,7 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
for (i = 0; i < PIV_NUM_KEYS; i++) {
struct sc_pkcs15_prkey_info prkey_info;
struct sc_pkcs15_object prkey_obj;
u8 pin_policy = prkeys[i].ref;

memset(&prkey_info, 0, sizeof(prkey_info));
memset(&prkey_obj, 0, sizeof(prkey_obj));
Expand All @@ -1166,8 +1167,21 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
prkey_obj.flags = prkeys[i].obj_flags;
prkey_obj.user_consent = prkeys[i].user_consent; /* only Sign key */

if (prkeys[i].auth_id)
sc_card_ctl(p15card->card, SC_CARDCTL_PIV_YK_PIN_POLICY, &pin_policy);
frankmorgner marked this conversation as resolved.
Show resolved Hide resolved
switch (pin_policy) {
case 0x02:
/* PIN is checked once for the session */
prkey_obj.user_consent = 0;
break;
case 0x03:
/* PIN is verified just before operation */
prkey_obj.user_consent = 1;
}
/* PIN is never checked for operations if PIN policy is set to 0x01 */
if (prkeys[i].auth_id && pin_policy != 0x01)
sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id);
else
prkey_obj.flags &= ~SC_PKCS15_CO_FLAG_PRIVATE;

/*
* When no cert is present and a pubkey in a file was found,
Expand Down