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
+654 -46
View File
@@ -34,6 +34,7 @@
#include <stdint.h>
#include <ctype.h>
#include "des.h"
#include <openssl/des.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
@@ -62,26 +63,29 @@ const uint8_t CCC_TMPL[] = {
#define CCC_ID_OFFS 9
#define CB_CCC_ID 14
#define TAG_CERT 0x70
#define TAG_CERT_COMPRESS 0x71
#define TAG_CERT_LRC 0xFE
#define TAG_PIVMAN_DATA 0x80
#define TAG_FLAGS_1 0x81
#define TAG_SALT 0x82
#define TAG_PIN_TIMESTAMP 0x83
#define TAG_MSCMAP 0x81
#define TAG_MSROOTS_END 0x82
#define TAG_MSROOTS_MID 0x83
#define TAG_CERT 0x70
#define TAG_CERT_COMPRESS 0x71
#define TAG_CERT_LRC 0xFE
#define TAG_ADMIN 0x80
#define TAG_ADMIN_FLAGS_1 0x81
#define TAG_ADMIN_SALT 0x82
#define TAG_ADMIN_TIMESTAMP 0x83
#define TAG_PROTECTED 0x88
#define TAG_PROTECTED_FLAGS_1 0x81
#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_EXP 0x82
#define TAG_ECC_POINT 0x86
#define TAG_RSA_MODULUS 0x81
#define TAG_RSA_EXP 0x82
#define TAG_ECC_POINT 0x86
#define CB_ECC_POINTP256 65
#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_MSCMAP 0x5fff10
#define YKPIV_OBJ_MSROOTS1 0x5fff11
@@ -90,45 +94,24 @@ const uint8_t CCC_TMPL[] = {
#define YKPIV_OBJ_MSROOTS4 0x5fff14
#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_MAX (CB_OBJ_TAG_MIN + 2) // 1 byte tag + 3 bytes len
typedef enum {
PRNG_OK = 0,
PRNG_GENERAL_ERROR = -1
} prng_rc;
#define member_size(type, member) sizeof(((type*)0)->member)
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);
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) {
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_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
*/
@@ -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);
}
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 res = YKPIV_OK;
uint8_t buf[CB_BUF_MAX];
@@ -857,6 +905,327 @@ Cleanup:
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) {
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) {
// TREV TODO: should this select application?
ykpiv_rc res = YKPIV_OK;
uint8_t *ptr = NULL;
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) {
// TREV TODO: should this select application?
uint8_t buf[CB_OBJ_MAX];
size_t cbBuf = sizeof(buf);
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
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;
}