Backport from minidriver:

commit 90020fea0ac34b2f98b68a5798fa85cb5ad12175 (tag: 3.2)
Author: Dave Pate <dpate@yubico.com>
Date:   Thu Jul 27 00:31:54 2017 -0700

    Release 3.2
    Adds automatic PUK blocking
    Adds feature to turn automatic PUK blocking off
    Miscellaneous fixes with metadata handling
This commit is contained in:
Trevor Bentley
2017-08-28 12:32:56 +02:00
parent 06f2e777ba
commit fb00baf672
6 changed files with 873 additions and 130 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ AM_CPPFLAGS = $(OPENSSL_CFLAGS) $(PCSC_CFLAGS)
lib_LTLIBRARIES = libykpiv.la lib_LTLIBRARIES = libykpiv.la
libykpiv_la_SOURCES = ykpiv.c util.c version.c ykpiv.pc.in ykpiv.map internal.h libykpiv_la_SOURCES = ykpiv.c util.c des.c des.h version.c ykpiv.pc.in ykpiv.map internal.h
libykpiv_la_SOURCES += error.c libykpiv_la_SOURCES += error.c
libykpiv_la_includedir = $(includedir)/ykpiv libykpiv_la_includedir = $(includedir)/ykpiv
libykpiv_la_include_HEADERS = ykpiv.h ykpiv-version.h libykpiv_la_include_HEADERS = ykpiv.h ykpiv-version.h
+6 -6
View File
@@ -55,12 +55,8 @@ struct ykpiv_state {
SCARDHANDLE card; SCARDHANDLE card;
int verbose; int verbose;
char *pin; char *pin;
ykpiv_allocator allocator; ykpiv_allocator allocator;
bool isNEO; bool isNEO;
uint8_t mgmKey[CB_MGM_KEY];
bool fMgmKeySet;
}; };
union u_APDU { union u_APDU {
@@ -93,7 +89,11 @@ extern unsigned const char aid[];
#define CB_ATR_MAX 33 #define CB_ATR_MAX 33
#define ATR_NEO_R3 "\x3b\xfc\x13\x00\x00\x81\x31\xfe\x15\x59\x75\x62\x69\x6b\x65\x79\x4e\x45\x4f\x72\x33\xe1" #define YKPIV_ATR_NEO_R3 "\x3b\xfc\x13\x00\x00\x81\x31\xfe\x15\x59\x75\x62\x69\x6b\x65\x79\x4e\x45\x4f\x72\x33\xe1"
#define ATR_YK4 "\x3b\xf8\x13\x00\x00\x81\x31\xfe\x15\x59\x75\x62\x69\x6b\x65\x79\x34\xd4" #define YKPIV_ATR_YK4 "\x3b\xf8\x13\x00\x00\x81\x31\xfe\x15\x59\x75\x62\x69\x6b\x65\x79\x34\xd4"
#define CHREF_ACT_CHANGE_PIN 0
#define CHREF_ACT_UNBLOCK_PIN 1
#define CHREF_ACT_CHANGE_PUK 2
#endif #endif
+6
View File
@@ -460,6 +460,12 @@ START_TEST(test_authenticate) {
res = ykpiv_authenticate(g_state, key); res = ykpiv_authenticate(g_state, key);
ck_assert_int_eq(res, YKPIV_OK); ck_assert_int_eq(res, YKPIV_OK);
// Verify same key works twice
res = ykpiv_hex_decode(default_mgm_key, strlen(default_mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK);
res = ykpiv_authenticate(g_state, key);
ck_assert_int_eq(res, YKPIV_OK);
// Change to new key // Change to new key
res = ykpiv_hex_decode(mgm_key, strlen(mgm_key), key, &key_len); res = ykpiv_hex_decode(mgm_key, strlen(mgm_key), key, &key_len);
ck_assert_int_eq(res, YKPIV_OK); ck_assert_int_eq(res, YKPIV_OK);
+654 -46
View File
@@ -34,6 +34,7 @@
#include <stdint.h> #include <stdint.h>
#include <ctype.h> #include <ctype.h>
#include "des.h"
#include <openssl/des.h> #include <openssl/des.h>
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/rsa.h> #include <openssl/rsa.h>
@@ -62,26 +63,29 @@ const uint8_t CCC_TMPL[] = {
#define CCC_ID_OFFS 9 #define CCC_ID_OFFS 9
#define CB_CCC_ID 14 #define CB_CCC_ID 14
#define TAG_CERT 0x70 #define TAG_CERT 0x70
#define TAG_CERT_COMPRESS 0x71 #define TAG_CERT_COMPRESS 0x71
#define TAG_CERT_LRC 0xFE #define TAG_CERT_LRC 0xFE
#define TAG_PIVMAN_DATA 0x80 #define TAG_ADMIN 0x80
#define TAG_FLAGS_1 0x81 #define TAG_ADMIN_FLAGS_1 0x81
#define TAG_SALT 0x82 #define TAG_ADMIN_SALT 0x82
#define TAG_PIN_TIMESTAMP 0x83 #define TAG_ADMIN_TIMESTAMP 0x83
#define TAG_MSCMAP 0x81 #define TAG_PROTECTED 0x88
#define TAG_MSROOTS_END 0x82 #define TAG_PROTECTED_FLAGS_1 0x81
#define TAG_MSROOTS_MID 0x83 #define TAG_PROTECTED_MGM 0x89
#define TAG_MSCMAP 0x81
#define TAG_MSROOTS_END 0x82
#define TAG_MSROOTS_MID 0x83
#define TAG_RSA_MODULUS 0x81 #define TAG_RSA_MODULUS 0x81
#define TAG_RSA_EXP 0x82 #define TAG_RSA_EXP 0x82
#define TAG_ECC_POINT 0x86 #define TAG_ECC_POINT 0x86
#define CB_ECC_POINTP256 65 #define CB_ECC_POINTP256 65
#define CB_ECC_POINTP384 97 #define CB_ECC_POINTP384 97
#define YKPIV_OBJ_PIVMAN_DATA 0x5fff00 #define YKPIV_OBJ_ADMIN_DATA 0x5fff00
#define YKPIV_OBJ_ATTESTATION 0x5fff01 #define YKPIV_OBJ_ATTESTATION 0x5fff01
#define YKPIV_OBJ_MSCMAP 0x5fff10 #define YKPIV_OBJ_MSCMAP 0x5fff10
#define YKPIV_OBJ_MSROOTS1 0x5fff11 #define YKPIV_OBJ_MSROOTS1 0x5fff11
@@ -90,45 +94,24 @@ const uint8_t CCC_TMPL[] = {
#define YKPIV_OBJ_MSROOTS4 0x5fff14 #define YKPIV_OBJ_MSROOTS4 0x5fff14
#define YKPIV_OBJ_MSROOTS5 0x5fff15 #define YKPIV_OBJ_MSROOTS5 0x5fff15
#define ADMIN_FLAGS_1_PUK_BLOCKED 0x01
#define ADMIN_FLAGS_1_PROTECTED_MGM 0x02
#define CB_ADMIN_SALT 16
#define CB_ADMIN_TIMESTAMP 4
#define ITER_MGM_PBKDF2 10000
#define PROTECTED_FLAGS_1_PUK_NOBLOCK 0x01
#define CB_OBJ_TAG_MIN 2 // 1 byte tag + 1 byte len #define CB_OBJ_TAG_MIN 2 // 1 byte tag + 1 byte len
#define CB_OBJ_TAG_MAX (CB_OBJ_TAG_MIN + 2) // 1 byte tag + 3 bytes len #define CB_OBJ_TAG_MAX (CB_OBJ_TAG_MIN + 2) // 1 byte tag + 3 bytes len
typedef enum { #define member_size(type, member) sizeof(((type*)0)->member)
PRNG_OK = 0,
PRNG_GENERAL_ERROR = -1
} prng_rc;
static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len); static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len);
static ykpiv_rc _write_certificate(ykpiv_state *state, uint8_t slot, uint8_t *data, size_t data_len); static ykpiv_rc _write_certificate(ykpiv_state *state, uint8_t slot, uint8_t *data, size_t data_len);
prng_rc prng_generate(unsigned char *buffer, const size_t cb_req) {
// TREV TODO: ykpiv.c needs to use this
prng_rc rc = PRNG_OK;
#ifdef _WINDOWS
HCRYPTPROV hProv = 0;
if (CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
if (!CryptGenRandom(hProv, (DWORD)cb_req, buffer)) {
rc = PRNG_GENERAL_ERROR;
}
CryptReleaseContext(hProv, 0);
}
else {
rc = PRNG_GENERAL_ERROR;
}
#else
if (-1 == RAND_pseudo_bytes(buffer, cb_req)) {
rc = PRNG_GENERAL_ERROR;
}
#endif
return rc;
}
static size_t _obj_size_max(ykpiv_state *state) { static size_t _obj_size_max(ykpiv_state *state) {
return (state && state->isNEO) ? CB_OBJ_MAX_NEO : CB_OBJ_MAX; return (state && state->isNEO) ? CB_OBJ_MAX_NEO : CB_OBJ_MAX;
} }
@@ -145,6 +128,11 @@ ykpiv_rc _ykpiv_begin_transaction(ykpiv_state *state);
ykpiv_rc _ykpiv_end_transaction(ykpiv_state *state); ykpiv_rc _ykpiv_end_transaction(ykpiv_state *state);
ykpiv_rc _ykpiv_ensure_application_selected(ykpiv_state *state); ykpiv_rc _ykpiv_ensure_application_selected(ykpiv_state *state);
static ykpiv_rc _read_metadata(ykpiv_state *state, uint8_t tag, uint8_t* data, size_t* pcb_data);
static ykpiv_rc _write_metadata(ykpiv_state *state, uint8_t tag, uint8_t *data, size_t cb_data);
static ykpiv_rc _get_metadata_item(uint8_t *data, size_t cb_data, uint8_t tag, uint8_t **pp_item, size_t *pcb_item);
static ykpiv_rc _set_metadata_item(uint8_t *data, size_t *pcb_data, size_t cb_data_max, uint8_t tag, uint8_t *p_item, size_t cb_item);
/* /*
** YKPIV Utility API - aggregate functions and slightly nicer interface ** YKPIV Utility API - aggregate functions and slightly nicer interface
*/ */
@@ -387,6 +375,66 @@ ykpiv_rc ykpiv_util_delete_cert(ykpiv_state *state, uint8_t slot) {
return ykpiv_util_write_cert(state, slot, NULL, 0); return ykpiv_util_write_cert(state, slot, NULL, 0);
} }
ykpiv_rc ykpiv_util_block_puk(ykpiv_state *state) {
ykpiv_rc res = YKPIV_OK;
uint8_t puk[] = { 0x30, 0x42, 0x41, 0x44, 0x46, 0x30, 0x30, 0x44 };
int tries = -1;
uint8_t data[CB_BUF_MAX];
size_t cb_data = sizeof(data);
uint8_t *p_item = NULL;
size_t cb_item = 0;
uint8_t flags = 0;
if (NULL == state) return YKPIV_GENERIC_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
while (tries != 0) {
if (YKPIV_OK == (res = ykpiv_change_puk(state, puk, sizeof(puk), puk, sizeof(puk), &tries))) {
/* did we accidentally choose the correct PUK?, change our puk and try again */
puk[0]++;
}
else {
/* depending on the firmware, tries may not be set to zero when the PUK is blocked, */
/* instead, the return code will be PIN_LOCKED and tries will be unset */
if (YKPIV_PIN_LOCKED == res) {
tries = 0;
res = YKPIV_OK;
}
}
}
/* attempt to set the puk blocked flag in admin data */
if (YKPIV_OK == _read_metadata(state, TAG_ADMIN, data, &cb_data)) {
if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_ADMIN_FLAGS_1, &p_item, &cb_item)) {
if (sizeof(flags) == cb_item) {
memcpy(&flags, p_item, cb_item);
}
else {
if (state->verbose) { fprintf(stderr, "admin flags exist, but are incorrect size = %zu", cb_item); }
}
}
}
flags |= ADMIN_FLAGS_1_PUK_BLOCKED;
if (YKPIV_OK != _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_ADMIN_FLAGS_1, (uint8_t*)&flags, sizeof(flags))) {
if (state->verbose) { fprintf(stderr, "could not set admin flags"); }
}
else {
if (YKPIV_OK != _write_metadata(state, TAG_ADMIN, data, cb_data)) {
if (state->verbose) { fprintf(stderr, "could not write admin metadata"); }
}
}
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
ykpiv_rc ykpiv_util_read_mscmap(ykpiv_state *state, ykpiv_container **containers, size_t *n_containers) { ykpiv_rc ykpiv_util_read_mscmap(ykpiv_state *state, ykpiv_container **containers, size_t *n_containers) {
ykpiv_rc res = YKPIV_OK; ykpiv_rc res = YKPIV_OK;
uint8_t buf[CB_BUF_MAX]; uint8_t buf[CB_BUF_MAX];
@@ -857,6 +905,327 @@ Cleanup:
return res; return res;
} }
ykpiv_rc ykpiv_util_get_config(ykpiv_state *state, ykpiv_config *config) {
ykpiv_rc res = YKPIV_OK;
uint8_t data[CB_BUF_MAX] = { 0 };
size_t cb_data = sizeof(data);
uint8_t *p_item = NULL;
size_t cb_item = 0;
if (NULL == state) return YKPIV_GENERIC_ERROR;
if (NULL == config) return YKPIV_GENERIC_ERROR;
// initialize default values
config->protected_data_available = false;
config->puk_blocked = false;
config->puk_noblock_on_upgrade = false;
config->pin_last_changed = 0;
config->mgm_type = YKPIV_CONFIG_MGM_MANUAL;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
/* recover admin data */
if (YKPIV_OK == _read_metadata(state, TAG_ADMIN, data, &cb_data)) {
if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_ADMIN_FLAGS_1, &p_item, &cb_item)) {
if (*p_item & ADMIN_FLAGS_1_PUK_BLOCKED) config->puk_blocked = true;
if (*p_item & ADMIN_FLAGS_1_PROTECTED_MGM) config->mgm_type = YKPIV_CONFIG_MGM_PROTECTED;
}
if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_ADMIN_SALT, &p_item, &cb_item)) {
if (config->mgm_type != YKPIV_CONFIG_MGM_MANUAL) {
if (state->verbose) {
fprintf(stderr, "conflicting types of mgm key administration configured\n");
}
}
else {
config->mgm_type = YKPIV_CONFIG_MGM_DERIVED;
}
}
if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_ADMIN_TIMESTAMP, &p_item, &cb_item)) {
if (CB_ADMIN_TIMESTAMP != cb_item) {
if (state->verbose) {
fprintf(stderr, "pin timestamp in admin metadata is an invalid size");
}
}
else {
memcpy(&(config->pin_last_changed), p_item, cb_item);
}
}
}
/* recover protected data */
cb_data = sizeof(data);
if (YKPIV_OK == _read_metadata(state, TAG_PROTECTED, data, &cb_data)) {
config->protected_data_available = true;
if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_PROTECTED_FLAGS_1, &p_item, &cb_item)) {
if (*p_item & PROTECTED_FLAGS_1_PUK_NOBLOCK) config->puk_noblock_on_upgrade = true;
}
if (YKPIV_OK == _get_metadata_item(data, cb_data, TAG_PROTECTED_MGM, &p_item, &cb_item)) {
if (config->mgm_type != YKPIV_CONFIG_MGM_PROTECTED) {
if (state->verbose) {
fprintf(stderr, "conflicting types of mgm key administration configured - protected mgm exists\n");
}
}
config->mgm_type = YKPIV_CONFIG_MGM_PROTECTED; /* always favor protected mgm */
}
}
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
ykpiv_rc ykpiv_util_set_pin_last_changed(ykpiv_state *state) {
ykpiv_rc res = YKPIV_OK;
ykpiv_rc ykrc = YKPIV_OK;
uint8_t data[CB_BUF_MAX] = { 0 };
size_t cb_data = sizeof(data);
time_t tnow = 0;
if (NULL == state) return YKPIV_GENERIC_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
/* recover admin data */
if (YKPIV_OK != (ykrc = _read_metadata(state, TAG_ADMIN, data, &cb_data))) {
cb_data = 0; /* set current metadata blob size to zero, we'll add the timestamp to the blank blob */
}
tnow = time(NULL);
if (YKPIV_OK != (res = _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_ADMIN_TIMESTAMP, (uint8_t*)&tnow, CB_ADMIN_TIMESTAMP))) {
if (state->verbose) fprintf(stderr, "could not set pin timestamp, err = %d\n", res);
}
else {
if (YKPIV_OK != (res = _write_metadata(state, TAG_ADMIN, data, cb_data))) {
/* Note: this can fail if authenticate() wasn't called previously - expected behavior */
if (state->verbose) fprintf(stderr, "could not write admin data, err = %d\n", res);
}
}
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
ykpiv_rc ykpiv_util_get_derived_mgm(ykpiv_state *state, const uint8_t *pin, const size_t pin_len, ykpiv_mgm *mgm) {
ykpiv_rc res = YKPIV_OK;
pkcs5_rc p5rc = PKCS5_OK;
uint8_t data[CB_BUF_MAX] = { 0 };
size_t cb_data = sizeof(data);
uint8_t *p_item = NULL;
size_t cb_item = 0;
if (NULL == state) return YKPIV_GENERIC_ERROR;
if ((NULL == pin) || (0 == pin_len) || (NULL == mgm)) return YKPIV_GENERIC_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
/* recover management key */
if (YKPIV_OK == (res = _read_metadata(state, TAG_ADMIN, data, &cb_data))) {
if (YKPIV_OK == (res = _get_metadata_item(data, cb_data, TAG_ADMIN_SALT, &p_item, &cb_item))) {
if (cb_item != CB_ADMIN_SALT) {
if (state->verbose) fprintf(stderr, "derived mgm salt exists, but is incorrect size = %zu\n", cb_item);
res = YKPIV_GENERIC_ERROR;
goto Cleanup;
}
if (PKCS5_OK != (p5rc = pkcs5_pbkdf2_sha1(pin, pin_len, p_item, cb_item, ITER_MGM_PBKDF2, mgm->data, member_size(ykpiv_mgm, data)))) {
if (state->verbose) fprintf(stderr, "pbkdf2 failure, err = %d\n", p5rc);
res = YKPIV_GENERIC_ERROR;
goto Cleanup;
}
}
}
Cleanup:
_ykpiv_end_transaction(state);
return res;
}
ykpiv_rc ykpiv_util_get_protected_mgm(ykpiv_state *state, ykpiv_mgm *mgm) {
ykpiv_rc res = YKPIV_OK;
uint8_t data[CB_BUF_MAX] = { 0 };
size_t cb_data = sizeof(data);
uint8_t *p_item = NULL;
size_t cb_item = 0;
if (NULL == state) return YKPIV_GENERIC_ERROR;
if (NULL == mgm) return YKPIV_GENERIC_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
if (YKPIV_OK != (res = _read_metadata(state, TAG_PROTECTED, data, &cb_data))) {
if (state->verbose) fprintf(stderr, "could not read protected data, err = %d\n", res);
goto Cleanup;
}
if (YKPIV_OK != (res = _get_metadata_item(data, cb_data, TAG_PROTECTED_MGM, &p_item, &cb_item))) {
if (state->verbose) fprintf(stderr, "could not read protected mgm from metadata, err = %d\n", res);
goto Cleanup;
}
if (cb_item != member_size(ykpiv_mgm, data)) {
if (state->verbose) fprintf(stderr, "protected data contains mgm, but is the wrong size = %zu\n", cb_item);
res = YKPIV_AUTHENTICATION_ERROR;
goto Cleanup;
}
memcpy(mgm->data, p_item, cb_item);
Cleanup:
memset(data, 0, sizeof(data));
_ykpiv_end_transaction(state);
return res;
}
/* to set a generated mgm, pass NULL for mgm, or set mgm.data to all zeroes */
ykpiv_rc ykpiv_util_set_protected_mgm(ykpiv_state *state, ykpiv_mgm *mgm) {
ykpiv_rc res = YKPIV_OK;
ykpiv_rc ykrc = YKPIV_OK;
prng_rc prngrc = PRNG_OK;
bool fGenerate = false;
uint8_t mgm_key[member_size(ykpiv_mgm, data)] = { 0 };
size_t i = 0;
uint8_t data[CB_BUF_MAX] = { 0 };
size_t cb_data = sizeof(data);
uint8_t *p_item = NULL;
size_t cb_item = 0;
uint8_t flags_1 = 0;
if (NULL == state) return YKPIV_GENERIC_ERROR;
if (!mgm) {
fGenerate = true;
}
else {
fGenerate = true;
memcpy(mgm_key, mgm->data, sizeof(mgm_key));
for (i = 0; i < sizeof(mgm_key); i++) {
if (mgm_key[i] != 0) {
fGenerate = false;
break;
}
}
}
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
/* try to set the mgm key as long as we don't encounter a fatal error */
do {
if (fGenerate) {
/* generate a new mgm key */
if (PRNG_OK != (prngrc = prng_generate(mgm_key, sizeof(mgm_key)))) {
if (state->verbose) fprintf(stderr, "could not set generate new mgm, err = %d\n", prngrc);
res = YKPIV_RANDOMNESS_ERROR;
goto Cleanup;
}
}
if (YKPIV_OK != (ykrc = ykpiv_set_mgmkey(state, mgm_key))) {
/*
** if _set_mgmkey fails with YKPIV_KEY_ERROR, it means the generated key is weak
** otherwise, log a warning, since the device mgm key is corrupt or we're in
** a state where we can't set the mgm key
*/
if (YKPIV_KEY_ERROR != ykrc) {
if (state->verbose) fprintf(stderr, "could not set new derived mgm key, err = %d\n", ykrc);
res = ykrc;
goto Cleanup;
}
}
else {
/* _set_mgmkey succeeded, stop generating */
fGenerate = false;
}
} while (fGenerate);
/* set output mgm */
if (mgm) {
memcpy(mgm->data, mgm_key, sizeof(mgm_key));
}
/* after this point, we've set the mgm key, so the function should succeed, regardless of being able to set the metadata */
/* set the new mgm key in protected data */
if (YKPIV_OK != (ykrc = _read_metadata(state, TAG_PROTECTED, data, &cb_data))) {
cb_data = 0; /* set current metadata blob size to zero, we'll add to the blank blob */
}
if (YKPIV_OK != (ykrc = _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_PROTECTED_MGM, mgm_key, sizeof(mgm_key)))) {
if (state->verbose) fprintf(stderr, "could not set protected mgm item, err = %d\n", ykrc);
}
else {
if (YKPIV_OK != (ykrc = _write_metadata(state, TAG_PROTECTED, data, cb_data))) {
if (state->verbose) fprintf(stderr, "could not write protected data, err = %d\n", ykrc);
goto Cleanup;
}
}
/* set the protected mgm flag in admin data */
cb_data = sizeof(data);
if (YKPIV_OK != (ykrc = _read_metadata(state, TAG_ADMIN, data, &cb_data))) {
cb_data = 0;
}
else {
if (YKPIV_OK != (ykrc = _get_metadata_item(data, cb_data, TAG_ADMIN_FLAGS_1, &p_item, &cb_item))) {
/* flags are not set */
if (state->verbose) fprintf(stderr, "admin data exists, but flags are not present\n");
}
if (cb_item == sizeof(flags_1)) {
memcpy(&flags_1, p_item, cb_item);
}
else {
if (state->verbose) fprintf(stderr, "admin data flags are an incorrect size = %zu\n", cb_item);
}
/* remove any existing salt */
if (YKPIV_OK != (ykrc = _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_ADMIN_SALT, NULL, 0))) {
if (state->verbose) fprintf(stderr, "could not unset derived mgm salt, err = %d\n", ykrc);
}
}
flags_1 |= ADMIN_FLAGS_1_PROTECTED_MGM;
if (YKPIV_OK != (ykrc = _set_metadata_item(data, &cb_data, CB_OBJ_MAX, TAG_ADMIN_FLAGS_1, &flags_1, sizeof(flags_1)))) {
if (state->verbose) fprintf(stderr, "could not set admin flags item, err = %d\n", ykrc);
}
else {
if (YKPIV_OK != (ykrc = _write_metadata(state, TAG_ADMIN, data, cb_data))) {
if (state->verbose) fprintf(stderr, "could not write admin data, err = %d\n", ykrc);
goto Cleanup;
}
}
Cleanup:
memset(data, 0, sizeof(data));
memset(mgm_key, 0, sizeof(mgm_key));
_ykpiv_end_transaction(state);
return res;
}
ykpiv_rc ykpiv_util_reset(ykpiv_state *state) { ykpiv_rc ykpiv_util_reset(ykpiv_state *state) {
unsigned char templ[] = {0, YKPIV_INS_RESET, 0, 0}; unsigned char templ[] = {0, YKPIV_INS_RESET, 0, 0};
@@ -908,6 +1277,7 @@ static int _slot2object(uint8_t slot) {
} }
static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len) { static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf, size_t *buf_len) {
// TREV TODO: should this select application?
ykpiv_rc res = YKPIV_OK; ykpiv_rc res = YKPIV_OK;
uint8_t *ptr = NULL; uint8_t *ptr = NULL;
int object_id = _slot2object(slot); int object_id = _slot2object(slot);
@@ -947,6 +1317,7 @@ static ykpiv_rc _read_certificate(ykpiv_state *state, uint8_t slot, uint8_t *buf
} }
static ykpiv_rc _write_certificate(ykpiv_state *state, uint8_t slot, uint8_t *data, size_t data_len) { static ykpiv_rc _write_certificate(ykpiv_state *state, uint8_t slot, uint8_t *data, size_t data_len) {
// TREV TODO: should this select application?
uint8_t buf[CB_OBJ_MAX]; uint8_t buf[CB_OBJ_MAX];
size_t cbBuf = sizeof(buf); size_t cbBuf = sizeof(buf);
int object_id = _slot2object(slot); int object_id = _slot2object(slot);
@@ -990,3 +1361,240 @@ static ykpiv_rc _write_certificate(ykpiv_state *state, uint8_t slot, uint8_t *da
// write onto device // write onto device
return ykpiv_save_object(state, object_id, buf, offset); return ykpiv_save_object(state, object_id, buf, offset);
} }
/*
** PIV Manager data helper functions
**
** These functions allow the PIV Manager to extend the YKPIV_OBJ_ADMIN_DATA object without having to change
** this implementation. New items may be added without modifying these functions. Data items are picked
** from the pivman_data buffer by tag, and replaced either in place if length allows or the data object is
** expanded to fit a new/updated data item.
*/
/*
** _get_metadata_item
**
** Parses the metadata blob, specified by data, looking for the specified tag. If found, the item is
** returned in pp_item and its size in pcb_item.
**
** If the item is not found, this function returns YKPIV_GENERIC_ERROR.
*/
static ykpiv_rc _get_metadata_item(uint8_t *data, size_t cb_data, uint8_t tag, uint8_t **pp_item, size_t *pcb_item) {
uint8_t *p_temp = data;
size_t cb_temp = 0;
uint8_t tag_temp = 0;
if (!data || !pp_item || !pcb_item) return YKPIV_GENERIC_ERROR;
*pp_item = NULL;
*pcb_item = 0;
while (p_temp < (data + cb_data)) {
tag_temp = *p_temp++;
p_temp += _ykpiv_get_length(p_temp, &cb_temp);
if (tag_temp == tag) {
// found tag
break;
}
p_temp += cb_temp;
}
if (p_temp < (data + cb_data)) {
*pp_item = p_temp;
*pcb_item = cb_temp;
return YKPIV_OK;
}
return YKPIV_GENERIC_ERROR;
}
static int _get_length_size(size_t length) {
if (length < 0x80) {
return 1;
}
else if (length < 0xff) {
return 2;
}
else {
return 3;
}
}
/*
** _set_metadata_item
**
** Adds or replaces a data item encoded in a metadata blob, specified by tag to the existing
** metadata blob (data) until it reaches the a maximum buffer size (cb_data_max).
**
** If adding/replacing the item would exceed cb_data_max, this function returns YKPIV_GENERIC_ERROR.
**
** The new size of the blob is returned in pcb_data.
*/
static ykpiv_rc _set_metadata_item(uint8_t *data, size_t *pcb_data, size_t cb_data_max, uint8_t tag, uint8_t *p_item, size_t cb_item) {
uint8_t *p_temp = data;
size_t cb_temp = 0;
uint8_t tag_temp = 0;
size_t cb_len = 0;
uint8_t *p_next = NULL;
long cb_moved = 0; /* must be signed to have negative offsets */
if (!data || !pcb_data) return YKPIV_GENERIC_ERROR;
while (p_temp < (data + *pcb_data)) {
tag_temp = *p_temp++;
cb_len = _ykpiv_get_length(p_temp, &cb_temp);
p_temp += cb_len;
if (tag_temp == tag) {
/* found tag */
/* check length, if it matches, overwrite */
if (cb_temp == cb_item) {
memcpy(p_temp, p_item, cb_item);
return YKPIV_OK;
}
/* length doesn't match, expand/shrink to fit */
p_next = p_temp + cb_temp;
cb_moved = (long)cb_item - (long)cb_temp + ((long)(cb_item != 0 ? _get_length_size(cb_item) : -1 /* for tag, if deleting */) - (long)cb_len); /* accounts for different length encoding */
/* length would cause buffer overflow, return error */
if (*pcb_data + cb_moved > cb_data_max) {
return YKPIV_GENERIC_ERROR;
}
/* move remaining data */
memmove(p_next + cb_moved, p_next, *pcb_data - (p_next - data));
*pcb_data += cb_moved;
/* re-encode item and insert */
if (cb_item != 0) {
p_temp -= cb_len;
p_temp += _ykpiv_set_length(p_temp, cb_item);
memcpy(p_temp, p_item, cb_item);
}
return YKPIV_OK;
} //if tag found
p_temp += cb_temp;
}
if (cb_item == 0) {
/* we've been asked to delete an existing item that isn't in the blob */
return YKPIV_OK;
}
// we did not find an existing tag, append
p_temp = data + *pcb_data;
cb_len = _get_length_size(cb_item);
// length would cause buffer overflow, return error
if (*pcb_data + cb_len + cb_item > cb_data_max) {
return YKPIV_GENERIC_ERROR;
}
*p_temp++ = tag;
p_temp += _ykpiv_set_length(p_temp, cb_item);
memcpy(p_temp, p_item, cb_item);
*pcb_data += 1 + cb_len + cb_item;
return YKPIV_OK;
}
/*
** _read_metadata
**
** Reads admin or protected data (specified by tag) from its associated object.
**
** The data stored in the object is parsed to ensure it has the correct tag and valid length.
**
** data must point to a buffer of at least CB_BUF_MAX bytes, and pcb_data should point to
** the size of data.
**
** To read from protected data, the pin must be verified prior to calling this function.
*/
static ykpiv_rc _read_metadata(ykpiv_state *state, uint8_t tag, uint8_t* data, size_t* pcb_data) {
// TREV TODO: should this select application?
ykpiv_rc res = YKPIV_OK;
uint8_t *p_temp = NULL;
size_t cb_temp = 0;
int obj_id = 0;
if (!data || !data || !pcb_data || (CB_BUF_MAX > *pcb_data)) return YKPIV_GENERIC_ERROR;
switch (tag) {
case TAG_ADMIN: obj_id = YKPIV_OBJ_ADMIN_DATA; break;
case TAG_PROTECTED: obj_id = YKPIV_OBJ_PRINTED; break;
default: return YKPIV_INVALID_OBJECT;
}
cb_temp = *pcb_data;
*pcb_data = 0;
if (YKPIV_OK != (res = ykpiv_fetch_object(state, obj_id, data, (unsigned long*)&cb_temp))) {
return res;
}
if (cb_temp < CB_OBJ_TAG_MIN) return YKPIV_GENERIC_ERROR;
p_temp = data;
if (tag != *p_temp++) return YKPIV_GENERIC_ERROR;
p_temp += _ykpiv_get_length(p_temp, pcb_data);
if (*pcb_data > ((size_t)cb_temp - (p_temp - data))) {
*pcb_data = 0;
return YKPIV_GENERIC_ERROR;
}
memmove(data, p_temp, *pcb_data);
return YKPIV_OK;
}
/*
** _write_metadata
**
** Writes admin/protected data, specified by tag to its associated object.
**
** To delete the metadata, set data to NULL and cb_data to 0.
**
** To write protected data, the pin must be verified prior to calling this function.
*/
static ykpiv_rc _write_metadata(ykpiv_state *state, uint8_t tag, uint8_t *data, size_t cb_data) {
// TREV TODO: should this select application?
ykpiv_rc res = YKPIV_OK;
uint8_t buf[CB_OBJ_MAX] = { 0 };
uint8_t *pTemp = buf;
int obj_id = 0;
if (cb_data > (_obj_size_max(state) - CB_OBJ_TAG_MAX)) {
return YKPIV_GENERIC_ERROR;
}
switch (tag) {
case TAG_ADMIN: obj_id = YKPIV_OBJ_ADMIN_DATA; break;
case TAG_PROTECTED: obj_id = YKPIV_OBJ_PRINTED; break;
default: return YKPIV_INVALID_OBJECT;
}
if (!data || (0 == cb_data)) {
// deleting metadata
res = ykpiv_save_object(state, obj_id, NULL, 0);
}
else {
*pTemp++ = tag;
pTemp += _ykpiv_set_length(pTemp, cb_data);
memcpy(pTemp, data, cb_data);
pTemp += cb_data;
res = ykpiv_save_object(state, obj_id, buf, pTemp - buf);
}
return res;
}
+109 -75
View File
@@ -34,14 +34,14 @@
#include <stdint.h> #include <stdint.h>
#include <ctype.h> #include <ctype.h>
#include <openssl/des.h> #include "des.h"
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include "internal.h" #include "internal.h"
#include "ykpiv.h" #include "ykpiv.h"
static ykpiv_rc send_data(ykpiv_state *state, APDU *apdu, #define YKPIV_MGM_DEFAULT "\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08"
static ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu,
unsigned char *data, unsigned long *recv_len, int *sw); unsigned char *data, unsigned long *recv_len, int *sw);
unsigned const char aid[] = { unsigned const char aid[] = {
@@ -202,7 +202,7 @@ ykpiv_rc _ykpiv_select_application(ykpiv_state *state) {
apdu.st.lc = sizeof(aid); apdu.st.lc = sizeof(aid);
memcpy(apdu.st.data, aid, sizeof(aid)); memcpy(apdu.st.data, aid, sizeof(aid));
if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { if((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
if(state->verbose) { if(state->verbose) {
fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res)); fprintf(stderr, "Failed communicating with card: '%s'\n", ykpiv_strerror(res));
} }
@@ -283,7 +283,7 @@ ykpiv_rc ykpiv_connect(ykpiv_state *state, const char *wanted) {
return YKPIV_PCSC_ERROR; return YKPIV_PCSC_ERROR;
} }
state->isNEO = (((sizeof(ATR_NEO_R3) - 1) == atr_len) && (0 == memcmp(ATR_NEO_R3, atr, atr_len))); state->isNEO = (((sizeof(YKPIV_ATR_NEO_R3) - 1) == atr_len) && (0 == memcmp(YKPIV_ATR_NEO_R3, atr, atr_len)));
} }
state->card = card; state->card = card;
@@ -434,7 +434,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ,
} }
apdu.st.lc = this_size; apdu.st.lc = this_size;
memcpy(apdu.st.data, in_ptr, this_size); memcpy(apdu.st.data, in_ptr, this_size);
res = send_data(state, &apdu, data, &recv_len, sw); res = _send_data(state, &apdu, data, &recv_len, sw);
if(res != YKPIV_OK) { if(res != YKPIV_OK) {
_ykpiv_end_transaction(state); _ykpiv_end_transaction(state);
return res; return res;
@@ -466,7 +466,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ,
memset(apdu.raw, 0, sizeof(apdu.raw)); memset(apdu.raw, 0, sizeof(apdu.raw));
apdu.st.ins = 0xc0; apdu.st.ins = 0xc0;
res = send_data(state, &apdu, data, &recv_len, sw); res = _send_data(state, &apdu, data, &recv_len, sw);
if(res != YKPIV_OK) { if(res != YKPIV_OK) {
_ykpiv_end_transaction(state); _ykpiv_end_transaction(state);
return res; return res;
@@ -485,7 +485,7 @@ ykpiv_rc ykpiv_transfer_data(ykpiv_state *state, const unsigned char *templ,
return _ykpiv_end_transaction(state); return _ykpiv_end_transaction(state);
} }
static ykpiv_rc send_data(ykpiv_state *state, APDU *apdu, static ykpiv_rc _send_data(ykpiv_state *state, APDU *apdu,
unsigned char *data, unsigned long *recv_len, int *sw) { unsigned char *data, unsigned long *recv_len, int *sw) {
long rc; long rc;
unsigned int send_len = (unsigned int)apdu->st.lc + 5; unsigned int send_len = (unsigned int)apdu->st.lc + 5;
@@ -519,24 +519,24 @@ static ykpiv_rc send_data(ykpiv_state *state, APDU *apdu,
ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) { ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) {
APDU apdu; APDU apdu;
unsigned char data[261]; unsigned char data[261];
DES_cblock challenge; unsigned char challenge[8];
unsigned long recv_len = sizeof(data); unsigned long recv_len = sizeof(data);
int sw; int sw;
ykpiv_rc res; ykpiv_rc res;
des_key* mgm_key = NULL;
size_t out_len = 0;
DES_key_schedule ks1, ks2, ks3; if (NULL == state) return YKPIV_GENERIC_ERROR;
// TREV TODO: default/derived key if (NULL == key) {
/* use the derived mgm key to authenticate, if it hasn't been derived, use default */
key = YKPIV_MGM_DEFAULT;
}
/* set up our key */ /* set up our key */
{ if (DES_OK != des_import_key(DES_TYPE_3DES, key, CB_MGM_KEY, &mgm_key)) {
const_DES_cblock key_tmp; res = YKPIV_ALGORITHM_ERROR;
memcpy(key_tmp, key, 8); goto Cleanup;
DES_set_key_unchecked(&key_tmp, &ks1);
memcpy(key_tmp, key + 8, 8);
DES_set_key_unchecked(&key_tmp, &ks2);
memcpy(key_tmp, key + 16, 8);
DES_set_key_unchecked(&key_tmp, &ks3);
} }
/* get a challenge from the card */ /* get a challenge from the card */
@@ -549,10 +549,12 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) {
apdu.st.data[0] = 0x7c; apdu.st.data[0] = 0x7c;
apdu.st.data[1] = 0x02; apdu.st.data[1] = 0x02;
apdu.st.data[2] = 0x80; apdu.st.data[2] = 0x80;
if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
return res; goto Cleanup;
} else if(sw != SW_SUCCESS) { }
return YKPIV_AUTHENTICATION_ERROR; else if (sw != SW_SUCCESS) {
res = YKPIV_AUTHENTICATION_ERROR;
goto Cleanup;
} }
memcpy(challenge, data + 4, 8); memcpy(challenge, data + 4, 8);
} }
@@ -560,8 +562,9 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) {
/* send a response to the cards challenge and a challenge of our own. */ /* send a response to the cards challenge and a challenge of our own. */
{ {
unsigned char *dataptr = apdu.st.data; unsigned char *dataptr = apdu.st.data;
DES_cblock response; unsigned char response[8];
DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 0); out_len = sizeof(response);
des_decrypt(mgm_key, challenge, sizeof(challenge), response, &out_len);
recv_len = sizeof(data); recv_len = sizeof(data);
memset(apdu.raw, 0, sizeof(apdu)); memset(apdu.raw, 0, sizeof(apdu));
@@ -576,32 +579,45 @@ ykpiv_rc ykpiv_authenticate(ykpiv_state *state, unsigned const char *key) {
dataptr += 8; dataptr += 8;
*dataptr++ = 0x81; *dataptr++ = 0x81;
*dataptr++ = 8; *dataptr++ = 8;
if(RAND_pseudo_bytes(dataptr, 8) == -1) { if (PRNG_GENERAL_ERROR == prng_generate(dataptr, 8)) {
if(state->verbose) { if (state->verbose) {
fprintf(stderr, "Failed getting randomness for authentication.\n"); fprintf(stderr, "Failed getting randomness for authentication.\n");
} }
return YKPIV_RANDOMNESS_ERROR; res = YKPIV_RANDOMNESS_ERROR;
goto Cleanup;
} }
memcpy(challenge, dataptr, 8); memcpy(challenge, dataptr, 8);
dataptr += 8; dataptr += 8;
apdu.st.lc = dataptr - apdu.st.data; apdu.st.lc = (unsigned char)(dataptr - apdu.st.data);
if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
return res; goto Cleanup;
} else if(sw != SW_SUCCESS) { }
return YKPIV_AUTHENTICATION_ERROR; else if (sw != SW_SUCCESS) {
res = YKPIV_AUTHENTICATION_ERROR;
goto Cleanup;
} }
} }
/* compare the response from the card with our challenge */ /* compare the response from the card with our challenge */
{ {
DES_cblock response; unsigned char response[8];
DES_ecb3_encrypt(&challenge, &response, &ks1, &ks2, &ks3, 1); out_len = sizeof(response);
if(memcmp(response, data + 4, 8) == 0) { des_encrypt(mgm_key, challenge, sizeof(challenge), response, &out_len);
return YKPIV_OK; if (memcmp(response, data + 4, 8) == 0) {
} else { res = YKPIV_OK;
return YKPIV_AUTHENTICATION_ERROR; }
else {
res = YKPIV_AUTHENTICATION_ERROR;
} }
} }
Cleanup:
if (mgm_key) {
des_destroy_key(mgm_key);
}
return res;
} }
ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) { ykpiv_rc ykpiv_set_mgmkey(ykpiv_state *state, const unsigned char *new_key) {
@@ -613,45 +629,43 @@ ykpiv_rc ykpiv_set_mgmkey2(ykpiv_state *state, const unsigned char *new_key, con
unsigned char data[261]; unsigned char data[261];
unsigned long recv_len = sizeof(data); unsigned long recv_len = sizeof(data);
int sw; int sw;
size_t i; ykpiv_rc res = YKPIV_OK;
ykpiv_rc res;
for(i = 0; i < 3; i++) { if (yk_des_is_weak_key(new_key, DES_LEN_3DES)) {
const_DES_cblock key_tmp; if (state->verbose) {
memcpy(key_tmp, new_key + i * 8, 8); fprintf(stderr, "Won't set new key '");
DES_set_odd_parity(&key_tmp); dump_hex(new_key, DES_LEN_3DES);
if(DES_is_weak_key(&key_tmp) != 0) { fprintf(stderr, "' since it's weak (with odd parity).\n");
if(state->verbose) {
fprintf(stderr, "Won't set new key '");
dump_hex(new_key + i * 8, 8);
fprintf(stderr, "' since it's weak (with parity the key is: ");
dump_hex(key_tmp, 8);
fprintf(stderr, ").\n");
}
return YKPIV_GENERIC_ERROR;
} }
return YKPIV_KEY_ERROR;
} }
memset(apdu.raw, 0, sizeof(apdu)); memset(apdu.raw, 0, sizeof(apdu));
apdu.st.ins = YKPIV_INS_SET_MGMKEY; apdu.st.ins = YKPIV_INS_SET_MGMKEY;
apdu.st.p1 = 0xff; apdu.st.p1 = 0xff;
if(touch == 0) { if (touch == 0) {
apdu.st.p2 = 0xff; apdu.st.p2 = 0xff;
} else if(touch == 1) { }
else if (touch == 1) {
apdu.st.p2 = 0xfe; apdu.st.p2 = 0xfe;
} else { }
else {
return YKPIV_GENERIC_ERROR; return YKPIV_GENERIC_ERROR;
} }
apdu.st.lc = DES_KEY_SZ * 3 + 3;
apdu.st.lc = DES_LEN_3DES + 3;
apdu.st.data[0] = YKPIV_ALGO_3DES; apdu.st.data[0] = YKPIV_ALGO_3DES;
apdu.st.data[1] = YKPIV_KEY_CARDMGM; apdu.st.data[1] = YKPIV_KEY_CARDMGM;
apdu.st.data[2] = DES_KEY_SZ * 3; apdu.st.data[2] = DES_LEN_3DES;
memcpy(apdu.st.data + 3, new_key, DES_KEY_SZ * 3); memcpy(apdu.st.data + 3, new_key, DES_LEN_3DES);
if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
if ((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
return res; return res;
} else if(sw == SW_SUCCESS) { }
else if (sw == SW_SUCCESS) {
return YKPIV_OK; return YKPIV_OK;
} }
return YKPIV_GENERIC_ERROR; return YKPIV_GENERIC_ERROR;
} }
@@ -793,16 +807,39 @@ ykpiv_rc ykpiv_sign_data(ykpiv_state *state,
const unsigned char *raw_in, size_t in_len, const unsigned char *raw_in, size_t in_len,
unsigned char *sign_out, size_t *out_len, unsigned char *sign_out, size_t *out_len,
unsigned char algorithm, unsigned char key) { unsigned char algorithm, unsigned char key) {
ykpiv_rc res = YKPIV_OK;
return _general_authenticate(state, raw_in, in_len, sign_out, out_len, if (NULL == state) return YKPIV_GENERIC_ERROR;
algorithm, key, false);
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
// TREV TODO: clean up selections
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
res = _general_authenticate(state, raw_in, in_len, sign_out, out_len,
algorithm, key, false);
Cleanup:
_ykpiv_end_transaction(state);
return res;
} }
ykpiv_rc ykpiv_decipher_data(ykpiv_state *state, const unsigned char *in, ykpiv_rc ykpiv_decipher_data(ykpiv_state *state, const unsigned char *in,
size_t in_len, unsigned char *out, size_t *out_len, size_t in_len, unsigned char *out, size_t *out_len,
unsigned char algorithm, unsigned char key) { unsigned char algorithm, unsigned char key) {
return _general_authenticate(state, in, in_len, out, out_len, ykpiv_rc res = YKPIV_OK;
algorithm, key, true);
if (NULL == state) return YKPIV_GENERIC_ERROR;
if (YKPIV_OK != (res = _ykpiv_begin_transaction(state))) return YKPIV_PCSC_ERROR;
if (YKPIV_OK != (res = _ykpiv_ensure_application_selected(state))) goto Cleanup;
res = _general_authenticate(state, in, in_len, out, out_len,
algorithm, key, true);
Cleanup:
_ykpiv_end_transaction(state);
return res;
} }
ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) { ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) {
@@ -814,7 +851,7 @@ ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) {
memset(apdu.raw, 0, sizeof(apdu)); memset(apdu.raw, 0, sizeof(apdu));
apdu.st.ins = YKPIV_INS_GET_VERSION; apdu.st.ins = YKPIV_INS_GET_VERSION;
if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { if((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
return res; return res;
} else if(sw == SW_SUCCESS) { } else if(sw == SW_SUCCESS) {
int result = snprintf(version, len, "%d.%d.%d", data[0], data[1], data[2]); int result = snprintf(version, len, "%d.%d.%d", data[0], data[1], data[2]);
@@ -828,6 +865,7 @@ ykpiv_rc ykpiv_get_version(ykpiv_state *state, char *version, size_t len) {
} }
ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) { ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) {
// TREV TODO: pin len?
APDU apdu; APDU apdu;
unsigned char data[261]; unsigned char data[261];
unsigned long recv_len = sizeof(data); unsigned long recv_len = sizeof(data);
@@ -853,7 +891,7 @@ ykpiv_rc ykpiv_verify(ykpiv_state *state, const char *pin, int *tries) {
memset(apdu.st.data + len, 0xff, 8 - len); memset(apdu.st.data + len, 0xff, 8 - len);
} }
} }
if((res = send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) { if((res = _send_data(state, &apdu, data, &recv_len, &sw)) != YKPIV_OK) {
return res; return res;
} else if(sw == SW_SUCCESS) { } else if(sw == SW_SUCCESS) {
if (pin) { if (pin) {
@@ -895,7 +933,7 @@ ykpiv_rc ykpiv_set_pin_retries(ykpiv_state *state, const int tries) {
unsigned char templ[] = {0, YKPIV_INS_SET_PIN_RETRIES, (unsigned char)tries, YKPIV_RETRIES_DEFAULT}; unsigned char templ[] = {0, YKPIV_INS_SET_PIN_RETRIES, (unsigned char)tries, YKPIV_RETRIES_DEFAULT};
unsigned char data[0xff]; unsigned char data[0xff];
unsigned long recv_len = sizeof(data); unsigned long recv_len = sizeof(data);
int sw; int sw = 0;
if (0 == tries) { if (0 == tries) {
//zero value means no change in retry count according to minidriver spec //zero value means no change in retry count according to minidriver spec
@@ -919,10 +957,6 @@ ykpiv_rc ykpiv_set_pin_retries(ykpiv_state *state, const int tries) {
return res; return res;
} }
#define CHREF_ACT_CHANGE_PIN 0
#define CHREF_ACT_UNBLOCK_PIN 1
#define CHREF_ACT_CHANGE_PUK 2
static ykpiv_rc change_pin_internal(ykpiv_state *state, int action, const char * current_pin, size_t current_pin_len, const char * new_pin, size_t new_pin_len, int *tries) { static ykpiv_rc change_pin_internal(ykpiv_state *state, int action, const char * current_pin, size_t current_pin_len, const char * new_pin, size_t new_pin_len, int *tries) {
int sw; int sw;
unsigned char templ[] = {0, YKPIV_INS_CHANGE_REFERENCE, 0, 0x80}; unsigned char templ[] = {0, YKPIV_INS_CHANGE_REFERENCE, 0, 0x80};
+96 -1
View File
@@ -70,7 +70,7 @@ extern "C"
ykpiv_pfn_alloc pfn_alloc; ykpiv_pfn_alloc pfn_alloc;
ykpiv_pfn_realloc pfn_realloc; ykpiv_pfn_realloc pfn_realloc;
ykpiv_pfn_free pfn_free; ykpiv_pfn_free pfn_free;
void * alloc_data; void * alloc_data;
} ykpiv_allocator; } ykpiv_allocator;
const char *ykpiv_strerror(ykpiv_rc err); const char *ykpiv_strerror(ykpiv_rc err);
@@ -308,6 +308,92 @@ extern "C"
ykpiv_rc ykpiv_util_read_msroots(ykpiv_state *state, uint8_t **data, size_t *data_len); ykpiv_rc ykpiv_util_read_msroots(ykpiv_state *state, uint8_t **data, size_t *data_len);
ykpiv_rc ykpiv_util_write_msroots(ykpiv_state *state, uint8_t *data, size_t data_len); ykpiv_rc ykpiv_util_write_msroots(ykpiv_state *state, uint8_t *data, size_t data_len);
typedef enum {
YKPIV_CONFIG_MGM_MANUAL = 0,
YKPIV_CONFIG_MGM_DERIVED = 1,
YKPIV_CONFIG_MGM_PROTECTED = 2
} ykpiv_config_mgm_type;
#pragma pack(push, 1)
typedef struct _ykpiv_config {
uint8_t protected_data_available;
uint8_t puk_blocked;
uint8_t puk_noblock_on_upgrade;
uint32_t pin_last_changed;
ykpiv_config_mgm_type mgm_type;
} ykpiv_config;
typedef struct _ykpiv_mgm {
uint8_t data[24];
} ykpiv_mgm;
#pragma pack(pop)
/**
* Get current PIV applet administration configuration state
*
* @param state [in] state
* @param config [out] output ykpiv_config struct with current applet data
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_get_config(ykpiv_state *state, ykpiv_config *config);
/**
* Set last pin changed time to current time
*
* The applet must be authenticated to call this function
*
* @param state state
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_set_pin_last_changed(ykpiv_state *state);
/**
* Get Derived MGM key
*
* @param state [in] state
* @param pin [in] pin used to derive mgm key
* @param pin_len [in] length of pin
* @param mgm [out] protected mgm key
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_get_derived_mgm(ykpiv_state *state, const uint8_t *pin, const size_t pin_len, ykpiv_mgm *mgm);
/**
* Get Protected MGM key
*
* The user pin must be verified to call this function
*
* @param state [in] state
* @param mgm [out] returns protected mgm key
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_get_protected_mgm(ykpiv_state *state, ykpiv_mgm *mgm);
/**
* Set Protected MGM key
*
* The applet must be authenticated and the user pin verified to call this function
*
* @param state state
* @param mgm [in] if mgm is NULL or mgm.data is all zeroes, generate mgm, otherwise set specified key; [out] returns generated mgm key
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_set_protected_mgm(ykpiv_state *state, ykpiv_mgm *mgm);
/**
* Reset PIV applet
*
* The user pin and puk must be blocked to call this function.
*
* @param state state
*
* @return ykpiv_rc error code
*/
ykpiv_rc ykpiv_util_reset(ykpiv_state *state); ykpiv_rc ykpiv_util_reset(ykpiv_state *state);
/** /**
@@ -352,6 +438,15 @@ extern "C"
*/ */
ykpiv_devmodel ykpiv_util_devicemodel(ykpiv_state *state); ykpiv_devmodel ykpiv_util_devicemodel(ykpiv_state *state);
/**
* Block PUK
*
* Utility function to block the PUK.
*
* To set the PUK blocked flag in the admin data, the applet must be authenticated.
*/
ykpiv_rc ykpiv_util_block_puk(ykpiv_state *state);
#ifdef __cplusplus #ifdef __cplusplus
} }