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:
+1
-1
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user