1710 lines
63 KiB
C
1710 lines
63 KiB
C
/*
|
|
* This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm).
|
|
* Copyright (c) 2022 Pol Henarejos.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "sc_hsm.h"
|
|
#include "file.h"
|
|
#include "libopensc/card-sc-hsm.h"
|
|
#include "random.h"
|
|
#include "common.h"
|
|
#include "mbedtls/sha256.h"
|
|
#include "mbedtls/aes.h"
|
|
#include "mbedtls/rsa.h"
|
|
#include "mbedtls/ecp.h"
|
|
#include "mbedtls/ecdsa.h"
|
|
#include "mbedtls/ecdh.h"
|
|
#include "mbedtls/cmac.h"
|
|
#include "mbedtls/hkdf.h"
|
|
#include "version.h"
|
|
|
|
const uint8_t sc_hsm_aid[] = {
|
|
11,
|
|
0xE8,0x2B,0x06,0x01,0x04,0x01,0x81,0xC3,0x1F,0x02,0x01
|
|
};
|
|
|
|
uint8_t session_pin[32], session_sopin[32];
|
|
bool has_session_pin = false, has_session_sopin = false;
|
|
static uint8_t dkeks = 0, current_dkeks = 0;
|
|
static uint8_t tmp_dkek[IV_SIZE+32];
|
|
|
|
static int sc_hsm_process_apdu();
|
|
|
|
static void init_sc_hsm();
|
|
static int sc_hsm_unload();
|
|
|
|
app_t *sc_hsm_select_aid(app_t *a) {
|
|
if (!memcmp(apdu.cmd_apdu_data, sc_hsm_aid+1, MIN(apdu.cmd_apdu_data_len,sc_hsm_aid[0]))) {
|
|
a->aid = sc_hsm_aid;
|
|
a->process_apdu = sc_hsm_process_apdu;
|
|
a->unload = sc_hsm_unload;
|
|
init_sc_hsm();
|
|
return a;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void __attribute__ ((constructor)) sc_hsm_ctor() {
|
|
register_app(sc_hsm_select_aid);
|
|
}
|
|
|
|
void init_sc_hsm() {
|
|
scan_flash();
|
|
has_session_pin = has_session_sopin = false;
|
|
isUserAuthenticated = false;
|
|
}
|
|
|
|
int sc_hsm_unload() {
|
|
has_session_pin = has_session_sopin = false;
|
|
isUserAuthenticated = false;
|
|
return HSM_OK;
|
|
}
|
|
|
|
static int encrypt(const uint8_t *key, const uint8_t *iv, uint8_t *data, int len)
|
|
{
|
|
mbedtls_aes_context aes;
|
|
mbedtls_aes_init(&aes);
|
|
uint8_t tmp_iv[IV_SIZE];
|
|
size_t iv_offset = 0;
|
|
memcpy(tmp_iv, iv, IV_SIZE);
|
|
int r = mbedtls_aes_setkey_enc (&aes, key, 256);
|
|
if (r != 0)
|
|
return HSM_EXEC_ERROR;
|
|
return mbedtls_aes_crypt_cfb128(&aes, MBEDTLS_AES_ENCRYPT, len, &iv_offset, tmp_iv, data, data);
|
|
}
|
|
|
|
static int decrypt(const uint8_t *key, const uint8_t *iv, uint8_t *data, int len)
|
|
{
|
|
mbedtls_aes_context aes;
|
|
mbedtls_aes_init(&aes);
|
|
uint8_t tmp_iv[IV_SIZE];
|
|
size_t iv_offset = 0;
|
|
memcpy(tmp_iv, iv, IV_SIZE);
|
|
int r = mbedtls_aes_setkey_enc (&aes, key, 256);
|
|
if (r != 0)
|
|
return HSM_EXEC_ERROR;
|
|
return mbedtls_aes_crypt_cfb128(&aes, MBEDTLS_AES_DECRYPT, len, &iv_offset, tmp_iv, data, data);
|
|
}
|
|
|
|
int load_dkek() {
|
|
if (has_session_pin == false)
|
|
return HSM_NO_LOGIN;
|
|
file_t *tf = search_by_fid(EF_DKEK, NULL, SPECIFY_EF);
|
|
if (!tf)
|
|
return HSM_ERR_FILE_NOT_FOUND;
|
|
memcpy(tmp_dkek, file_read(tf->data+sizeof(uint16_t)), IV_SIZE+32);
|
|
int ret = decrypt(session_pin, tmp_dkek, tmp_dkek+IV_SIZE, 32);
|
|
if (ret != 0)
|
|
return HSM_EXEC_ERROR;
|
|
return HSM_OK;
|
|
}
|
|
|
|
void release_dkek() {
|
|
memset(tmp_dkek, 0, sizeof(tmp_dkek));
|
|
}
|
|
|
|
void select_file(file_t *pe) {
|
|
if (!pe)
|
|
{
|
|
currentDF = (file_t *)MF;
|
|
currentEF = NULL;
|
|
}
|
|
else if (pe->type & FILE_TYPE_INTERNAL_EF) {
|
|
currentEF = pe;
|
|
currentDF = &file_entries[pe->parent];
|
|
}
|
|
else {
|
|
currentDF = pe;
|
|
}
|
|
if (currentEF == file_openpgp || currentEF == file_sc_hsm) {
|
|
selected_applet = currentEF;
|
|
//sc_hsm_unload(); //reset auth status
|
|
}
|
|
}
|
|
static int cmd_select() {
|
|
uint8_t p1 = P1(apdu);
|
|
uint8_t p2 = P2(apdu);
|
|
file_t *pe = NULL;
|
|
uint16_t fid = 0x0;
|
|
|
|
// Only "first or only occurence" supported
|
|
//if ((p2 & 0xF3) != 0x00) {
|
|
// return SW_INCORRECT_P1P2();
|
|
//}
|
|
|
|
if (apdu.cmd_apdu_data_len >= 2)
|
|
fid = get_uint16_t(apdu.cmd_apdu_data, 0);
|
|
|
|
if ((fid & 0xff00) == (PRKD_PREFIX << 8)) {
|
|
if (!(pe = search_dynamic_file(fid)))
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
else if ((fid & 0xff00) == (CD_PREFIX << 8)) {
|
|
if (!(pe = search_dynamic_file(fid)))
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
else if ((fid & 0xff00) == (EE_CERTIFICATE_PREFIX << 8)) {
|
|
if (!(pe = search_dynamic_file(fid)))
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
if (!pe) {
|
|
if (p1 == 0x0) { //Select MF, DF or EF - File identifier or absent
|
|
if (apdu.cmd_apdu_data_len == 0) {
|
|
pe = (file_t *)MF;
|
|
//ac_fini();
|
|
}
|
|
else if (apdu.cmd_apdu_data_len == 2) {
|
|
if (!(pe = search_by_fid(fid, NULL, SPECIFY_ANY))) {
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
}
|
|
}
|
|
else if (p1 == 0x01) { //Select child DF - DF identifier
|
|
if (!(pe = search_by_fid(fid, currentDF, SPECIFY_DF))) {
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
}
|
|
else if (p1 == 0x02) { //Select EF under the current DF - EF identifier
|
|
if (!(pe = search_by_fid(fid, currentDF, SPECIFY_EF))) {
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
}
|
|
else if (p1 == 0x03) { //Select parent DF of the current DF - Absent
|
|
if (apdu.cmd_apdu_data_len != 0)
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
else if (p1 == 0x04) { //Select by DF name - e.g., [truncated] application identifier
|
|
if (!(pe = search_by_name(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len))) {
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
if (card_terminated) {
|
|
return set_res_sw (0x62, 0x85);
|
|
}
|
|
}
|
|
else if (p1 == 0x08) { //Select from the MF - Path without the MF identifier
|
|
if (!(pe = search_by_path(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, MF))) {
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
}
|
|
else if (p1 == 0x09) { //Select from the current DF - Path without the current DF identifier
|
|
if (!(pe = search_by_path(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, currentDF))) {
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
}
|
|
}
|
|
if ((p2 & 0xfc) == 0x00 || (p2 & 0xfc) == 0x04) {
|
|
process_fci(pe);
|
|
}
|
|
else
|
|
return SW_INCORRECT_P1P2();
|
|
select_file(pe);
|
|
return SW_OK ();
|
|
}
|
|
|
|
int parse_token_info(const file_t *f, int mode) {
|
|
char *label = "SmartCard-HSM";
|
|
char *manu = "Pol Henarejos";
|
|
sc_pkcs15_tokeninfo_t *ti = (sc_pkcs15_tokeninfo_t *)calloc(1, sizeof(sc_pkcs15_tokeninfo_t));
|
|
ti->version = HSM_VERSION_MAJOR;
|
|
ti->flags = SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT;
|
|
ti->label = (char *)calloc(strlen(label)+1, sizeof(char));
|
|
strlcpy(ti->label, label, strlen(label)+1);
|
|
ti->serial_number = (char *)calloc(2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1, sizeof(char));
|
|
pico_get_unique_board_id_string(ti->serial_number, 2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1);
|
|
ti->manufacturer_id = (char *)calloc(strlen(manu)+1, sizeof(char));
|
|
strlcpy(ti->manufacturer_id, manu, strlen(manu)+1);
|
|
|
|
uint8_t *b;
|
|
size_t len;
|
|
int r = sc_pkcs15_encode_tokeninfo(NULL, ti, &b, &len);
|
|
if (mode == 1) {
|
|
memcpy(res_APDU, b, len);
|
|
res_APDU_size = len;
|
|
}
|
|
free(b);
|
|
sc_pkcs15_free_tokeninfo(ti);
|
|
return len;
|
|
}
|
|
|
|
|
|
static int cmd_list_keys()
|
|
{
|
|
//first CC
|
|
for (int i = 0; i < dynamic_files; i++) {
|
|
file_t *f = &dynamic_file[i];
|
|
if ((f->fid & 0xff00) == (PRKD_PREFIX << 8)) {
|
|
res_APDU[res_APDU_size++] = KEY_PREFIX;
|
|
res_APDU[res_APDU_size++] = f->fid & 0xff;
|
|
}
|
|
}
|
|
//second CD
|
|
for (int i = 0; i < dynamic_files; i++) {
|
|
file_t *f = &dynamic_file[i];
|
|
if ((f->fid & 0xff00) == (CD_PREFIX << 8)) {
|
|
res_APDU[res_APDU_size++] = CD_PREFIX;
|
|
res_APDU[res_APDU_size++] = f->fid & 0xff;
|
|
}
|
|
}
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_read_binary()
|
|
{
|
|
uint16_t fid;
|
|
uint32_t offset;
|
|
uint8_t ins = INS(apdu), p1 = P1(apdu), p2 = P2(apdu);
|
|
const file_t *ef = NULL;
|
|
|
|
if ((ins & 0x1) == 0)
|
|
{
|
|
if ((p1 & 0x80) != 0) {
|
|
if (!(ef = search_by_fid(p1&0x1f, NULL, SPECIFY_EF)))
|
|
return SW_FILE_NOT_FOUND ();
|
|
offset = p2;
|
|
}
|
|
else {
|
|
offset = make_uint16_t(p1, p2) & 0x7fff;
|
|
ef = currentEF;
|
|
}
|
|
}
|
|
else {
|
|
if (p1 == 0 && (p2 & 0xE0) == 0 && (p2 & 0x1f) != 0 && (p2 & 0x1f) != 0x1f) {
|
|
if (!(ef = search_by_fid(p2&0x1f, NULL, SPECIFY_EF)))
|
|
return SW_FILE_NOT_FOUND ();
|
|
}
|
|
else {
|
|
uint16_t file_id = make_uint16_t(p1, p2); // & 0x7fff;
|
|
if (file_id == 0x0)
|
|
ef = currentEF;
|
|
else if (!(ef = search_by_fid(file_id, NULL, SPECIFY_EF)) && !(ef = search_dynamic_file(file_id)))
|
|
return SW_FILE_NOT_FOUND ();
|
|
|
|
if (apdu.cmd_apdu_data[0] != 0x54)
|
|
return SW_WRONG_DATA();
|
|
|
|
offset = 0;
|
|
for (int d = 0; d < apdu.cmd_apdu_data[1]; d++)
|
|
offset |= apdu.cmd_apdu_data[2+d]<<(apdu.cmd_apdu_data[1]-1-d)*8;
|
|
}
|
|
}
|
|
|
|
if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) {
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
}
|
|
if (ef->data) {
|
|
if ((ef->type & FILE_DATA_FUNC) == FILE_DATA_FUNC) {
|
|
uint16_t data_len = ((int (*)(const file_t *, int))(ef->data))((const file_t *)ef, 1); //already copies content to res_APDU
|
|
if (offset > data_len)
|
|
return SW_WRONG_P1P2();
|
|
uint16_t maxle = data_len-offset;
|
|
if (apdu.expected_res_size > maxle)
|
|
apdu.expected_res_size = maxle;
|
|
if (offset) {
|
|
res_APDU += offset;
|
|
res_APDU_size -= offset;
|
|
}
|
|
}
|
|
else {
|
|
uint16_t data_len = file_read_uint16(ef->data);
|
|
if (offset > data_len)
|
|
return SW_WRONG_P1P2();
|
|
|
|
uint16_t maxle = data_len-offset;
|
|
if (apdu.expected_res_size > maxle)
|
|
apdu.expected_res_size = maxle;
|
|
res_APDU = file_read(ef->data+2+offset);
|
|
res_APDU_size = data_len-offset;
|
|
}
|
|
}
|
|
|
|
return SW_OK();
|
|
}
|
|
|
|
int pin_reset_retries(const file_t *pin, bool force) {
|
|
if (!pin)
|
|
return HSM_ERR_NULL_PARAM;
|
|
const file_t *max = search_by_fid(pin->fid+1, NULL, SPECIFY_EF);
|
|
const file_t *act = search_by_fid(pin->fid+2, NULL, SPECIFY_EF);
|
|
if (!max || !act)
|
|
return HSM_ERR_FILE_NOT_FOUND;
|
|
uint8_t retries = file_read_uint8(act->data+2);
|
|
if (retries == 0 && force == false) //blocked
|
|
return HSM_ERR_BLOCKED;
|
|
retries = file_read_uint8(max->data+2);
|
|
int r = flash_write_data_to_file((file_t *)act, &retries, sizeof(retries));
|
|
low_flash_available();
|
|
return r;
|
|
}
|
|
|
|
int pin_wrong_retry(const file_t *pin) {
|
|
if (!pin)
|
|
return HSM_ERR_NULL_PARAM;
|
|
const file_t *act = search_by_fid(pin->fid+2, NULL, SPECIFY_EF);
|
|
if (!act)
|
|
return HSM_ERR_FILE_NOT_FOUND;
|
|
uint8_t retries = file_read_uint8(act->data+2);
|
|
if (retries > 0) {
|
|
retries -= 1;
|
|
int r = flash_write_data_to_file((file_t *)act, &retries, sizeof(retries));
|
|
if (r != HSM_OK)
|
|
return r;
|
|
low_flash_available();
|
|
if (retries == 0)
|
|
return HSM_ERR_BLOCKED;
|
|
return retries;
|
|
}
|
|
return HSM_ERR_BLOCKED;
|
|
}
|
|
|
|
int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
|
|
if (!pin)
|
|
return SW_FILE_NOT_FOUND();
|
|
if (!pin->data) {
|
|
return SW_REFERENCE_NOT_FOUND();
|
|
}
|
|
isUserAuthenticated = false;
|
|
uint8_t dhash[32];
|
|
double_hash_pin(data, len, dhash);
|
|
if (sizeof(dhash) != file_read_uint16(pin->data)-1) //1 byte for pin len
|
|
return SW_CONDITIONS_NOT_SATISFIED();
|
|
if (memcmp(file_read(pin->data+3), dhash, sizeof(dhash)) != 0) {
|
|
uint8_t retries;
|
|
if ((retries = pin_wrong_retry(pin)) < HSM_OK)
|
|
return SW_PIN_BLOCKED();
|
|
return set_res_sw(0x63, 0xc0 | retries);
|
|
}
|
|
int r = pin_reset_retries(pin, false);
|
|
if (r == HSM_ERR_BLOCKED)
|
|
return SW_PIN_BLOCKED();
|
|
if (r != HSM_OK)
|
|
return SW_MEMORY_FAILURE();
|
|
isUserAuthenticated = true;
|
|
hash_multi(data, len, session_pin);
|
|
has_session_pin = true;
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_verify() {
|
|
uint8_t p1 = P1(apdu);
|
|
uint8_t p2 = P2(apdu);
|
|
|
|
if (p1 != 0x0 || (p2 & 0x60) != 0x0)
|
|
return SW_WRONG_P1P2();
|
|
uint8_t qualifier = p2&0x1f;
|
|
if (p2 == 0x81) { //UserPin
|
|
if (apdu.cmd_apdu_data_len > 0) {
|
|
return check_pin(file_pin1, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
|
|
}
|
|
if (file_read_uint8(file_retries_pin1->data+2) == 0)
|
|
return SW_PIN_BLOCKED();
|
|
return set_res_sw(0x63, 0xc0 | file_read_uint8(file_retries_pin1->data+2));
|
|
}
|
|
else if (p2 == 0x88) { //SOPin
|
|
}
|
|
return SW_REFERENCE_NOT_FOUND();
|
|
}
|
|
|
|
static int cmd_reset_retry() {
|
|
if (P1(apdu) == 0x0) {
|
|
if (P2(apdu) == 0x81) {
|
|
if (!file_sopin || !file_pin1) {
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
if (!file_sopin->data) {
|
|
return SW_REFERENCE_NOT_FOUND();
|
|
}
|
|
uint16_t r = check_pin(file_sopin, apdu.cmd_apdu_data, 8);
|
|
if (r != 0x9000)
|
|
return r;
|
|
uint8_t dhash[33];
|
|
dhash[0] = apdu.cmd_apdu_data_len-8;
|
|
double_hash_pin(apdu.cmd_apdu_data+8, apdu.cmd_apdu_data_len-8, dhash+1);
|
|
flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
|
|
if (pin_reset_retries(file_pin1, true) != HSM_OK)
|
|
return SW_MEMORY_FAILURE();
|
|
low_flash_available();
|
|
return SW_OK();
|
|
}
|
|
}
|
|
}
|
|
|
|
static int cmd_challenge() {
|
|
uint8_t *rb = (uint8_t *)random_bytes_get(apdu.expected_res_size);
|
|
if (!rb)
|
|
return SW_WRONG_LENGTH();
|
|
res_APDU = rb;
|
|
res_APDU_size = apdu.expected_res_size;
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_initialize() {
|
|
initialize_flash(true);
|
|
scan_flash();
|
|
dkeks = 0;
|
|
const uint8_t *p = apdu.cmd_apdu_data;
|
|
while (p-apdu.cmd_apdu_data < apdu.cmd_apdu_data_len) {
|
|
uint8_t tag = *p++;
|
|
uint8_t tag_len = *p++;
|
|
if (tag == 0x80) { //options
|
|
}
|
|
else if (tag == 0x81) { //user pin
|
|
if (file_pin1 && file_pin1->data) {
|
|
uint8_t dhash[33];
|
|
dhash[0] = tag_len;
|
|
double_hash_pin(p, tag_len, dhash+1);
|
|
flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
|
|
hash_multi(p, tag_len, session_pin);
|
|
has_session_pin = true;
|
|
}
|
|
}
|
|
else if (tag == 0x82) { //user pin
|
|
if (file_sopin && file_sopin->data) {
|
|
uint8_t dhash[33];
|
|
dhash[0] = tag_len;
|
|
double_hash_pin(p, tag_len, dhash+1);
|
|
flash_write_data_to_file(file_sopin, dhash, sizeof(dhash));
|
|
hash_multi(p, tag_len, session_sopin);
|
|
has_session_sopin = true;
|
|
}
|
|
}
|
|
else if (tag == 0x91) { //user pin
|
|
file_t *tf = search_by_fid(0x1082, NULL, SPECIFY_EF);
|
|
if (tf && tf->data) {
|
|
flash_write_data_to_file(tf, p, tag_len);
|
|
}
|
|
if (file_retries_pin1 && file_retries_pin1->data) {
|
|
flash_write_data_to_file(file_retries_pin1, p, tag_len);
|
|
}
|
|
}
|
|
else if (tag == 0x92) {
|
|
dkeks = *p;
|
|
current_dkeks = 0;
|
|
}
|
|
p += tag_len;
|
|
}
|
|
p = random_bytes_get(32);
|
|
memset(tmp_dkek, 0, sizeof(tmp_dkek));
|
|
memcpy(tmp_dkek, p, IV_SIZE);
|
|
if (dkeks == 0) {
|
|
p = random_bytes_get(32);
|
|
memcpy(tmp_dkek+IV_SIZE, p, 32);
|
|
encrypt(session_pin, tmp_dkek, tmp_dkek+IV_SIZE, 32);
|
|
file_t *tf = search_by_fid(EF_DKEK, NULL, SPECIFY_EF);
|
|
flash_write_data_to_file(tf, tmp_dkek, sizeof(tmp_dkek));
|
|
}
|
|
low_flash_available();
|
|
return SW_OK();
|
|
}
|
|
|
|
void double_hash_pin(const uint8_t *pin, size_t len, uint8_t output[32]) {
|
|
uint8_t o1[32];
|
|
hash_multi(pin, len, o1);
|
|
for (int i = 0; i < sizeof(o1); i++)
|
|
o1[i] ^= pin[i%len];
|
|
hash_multi(o1, sizeof(o1), output);
|
|
}
|
|
|
|
void hash_multi(const uint8_t *input, size_t len, uint8_t output[32])
|
|
{
|
|
mbedtls_sha256_context ctx;
|
|
mbedtls_sha256_init(&ctx);
|
|
int iters = 256;
|
|
|
|
pico_get_unique_board_id(&unique_id);
|
|
|
|
mbedtls_sha256_starts (&ctx, 0);
|
|
mbedtls_sha256_update (&ctx, unique_id.id, sizeof(unique_id.id));
|
|
|
|
while (iters > len)
|
|
{
|
|
mbedtls_sha256_update (&ctx, input, len);
|
|
iters -= len;
|
|
}
|
|
if (iters > 0) // remaining iterations
|
|
mbedtls_sha256_update (&ctx, input, iters);
|
|
mbedtls_sha256_finish (&ctx, output);
|
|
mbedtls_sha256_free (&ctx);
|
|
}
|
|
|
|
void hash(const uint8_t *input, size_t len, uint8_t output[32])
|
|
{
|
|
mbedtls_sha256_context ctx;
|
|
mbedtls_sha256_init(&ctx);
|
|
|
|
mbedtls_sha256_starts (&ctx, 0);
|
|
mbedtls_sha256_update (&ctx, input, len);
|
|
|
|
mbedtls_sha256_finish (&ctx, output);
|
|
mbedtls_sha256_free (&ctx);
|
|
}
|
|
|
|
void generic_hash(mbedtls_md_type_t md, const uint8_t *input, size_t len, uint8_t *output) {
|
|
mbedtls_md_context_t ctx;
|
|
mbedtls_md_init(&ctx);
|
|
const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md);
|
|
mbedtls_md_setup(&ctx, md_info, 0);
|
|
mbedtls_md_starts(&ctx);
|
|
mbedtls_md_update(&ctx, input, len);
|
|
mbedtls_md_finish(&ctx, output);
|
|
mbedtls_md_free(&ctx);
|
|
}
|
|
|
|
static int cmd_import_dkek() {
|
|
if (dkeks == 0)
|
|
return SW_COMMAND_NOT_ALLOWED();
|
|
if (has_session_pin == false)
|
|
return SW_CONDITIONS_NOT_SATISFIED();
|
|
file_t *tf = search_by_fid(EF_DKEK, NULL, SPECIFY_EF);
|
|
if (!authenticate_action(get_parent(tf), ACL_OP_CREATE_EF)) {
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
}
|
|
if (apdu.cmd_apdu_data_len > 0) {
|
|
for (int i = 0; i < apdu.cmd_apdu_data_len; i++)
|
|
tmp_dkek[IV_SIZE+i] ^= apdu.cmd_apdu_data[i];
|
|
if (++current_dkeks == dkeks) {
|
|
encrypt(session_pin, tmp_dkek, tmp_dkek+IV_SIZE, 32);
|
|
flash_write_data_to_file(tf, tmp_dkek, sizeof(tmp_dkek));
|
|
memset(tmp_dkek, 0, sizeof(tmp_dkek));
|
|
low_flash_available();
|
|
}
|
|
}
|
|
res_APDU[0] = dkeks;
|
|
res_APDU[1] = dkeks-current_dkeks;
|
|
//FNV hash
|
|
uint64_t hash = 0xcbf29ce484222325;
|
|
memcpy(tmp_dkek, file_read(tf->data+sizeof(uint16_t)), IV_SIZE+32);
|
|
decrypt(session_pin, tmp_dkek, tmp_dkek+IV_SIZE, 32);
|
|
for (int i = 0; i < 32; i++) {
|
|
hash ^= tmp_dkek[IV_SIZE+i];
|
|
hash *= 0x00000100000001B3;
|
|
}
|
|
memset(tmp_dkek, 0, sizeof(tmp_dkek));
|
|
memcpy(res_APDU+2,&hash,sizeof(hash));
|
|
res_APDU_size = 2+sizeof(hash);
|
|
return SW_OK();
|
|
}
|
|
|
|
struct ec_curve_mbed_id {
|
|
struct sc_lv_data curve;
|
|
mbedtls_ecp_group_id id;
|
|
};
|
|
struct ec_curve_mbed_id ec_curves_mbed[] = {
|
|
{ { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 24}, MBEDTLS_ECP_DP_SECP192R1 },
|
|
{ { (unsigned char *) "\xFF\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 32}, MBEDTLS_ECP_DP_SECP256R1 },
|
|
{ { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF", 48}, MBEDTLS_ECP_DP_SECP384R1 },
|
|
{ { (unsigned char *) "\x01\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 66}, MBEDTLS_ECP_DP_SECP521R1 },
|
|
{ { (unsigned char *) "\xA9\xFB\x57\xDB\xA1\xEE\xA9\xBC\x3E\x66\x0A\x90\x9D\x83\x8D\x72\x6E\x3B\xF6\x23\xD5\x26\x20\x28\x20\x13\x48\x1D\x1F\x6E\x53\x77", 32}, MBEDTLS_ECP_DP_BP256R1 },
|
|
{ { (unsigned char *) "\x8C\xB9\x1E\x82\xA3\x38\x6D\x28\x0F\x5D\x6F\x7E\x50\xE6\x41\xDF\x15\x2F\x71\x09\xED\x54\x56\xB4\x12\xB1\xDA\x19\x7F\xB7\x11\x23\xAC\xD3\xA7\x29\x90\x1D\x1A\x71\x87\x47\x00\x13\x31\x07\xEC\x53", 48}, MBEDTLS_ECP_DP_BP384R1 },
|
|
{ { (unsigned char *) "\xAA\xDD\x9D\xB8\xDB\xE9\xC4\x8B\x3F\xD4\xE6\xAE\x33\xC9\xFC\x07\xCB\x30\x8D\xB3\xB3\xC9\xD2\x0E\xD6\x63\x9C\xCA\x70\x33\x08\x71\x7D\x4D\x9B\x00\x9B\xC6\x68\x42\xAE\xCD\xA1\x2A\xE6\xA3\x80\xE6\x28\x81\xFF\x2F\x2D\x82\xC6\x85\x28\xAA\x60\x56\x58\x3A\x48\xF3", 64}, MBEDTLS_ECP_DP_BP512R1 },
|
|
{ { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xEE\x37", 24}, MBEDTLS_ECP_DP_SECP192K1 },
|
|
{ { (unsigned char *) "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\xFF\xFF\xFC\x2F", 32}, MBEDTLS_ECP_DP_SECP256K1 },
|
|
{ { NULL, 0 }, MBEDTLS_ECP_DP_NONE }
|
|
};
|
|
|
|
//Stores the private and public keys in flash
|
|
int store_keys(void *key_ctx, int type, uint8_t key_id, sc_context_t *ctx) {
|
|
int r, key_size;
|
|
uint8_t *asn1bin, *kdata;
|
|
size_t asn1len = 0;
|
|
if (type == SC_PKCS15_TYPE_PRKEY_RSA) {
|
|
mbedtls_rsa_context *rsa = (mbedtls_rsa_context *)key_ctx;
|
|
key_size = mbedtls_mpi_size(&rsa->P)+mbedtls_mpi_size(&rsa->Q);
|
|
kdata = (uint8_t *)calloc(1, key_size);
|
|
mbedtls_mpi_write_binary(&rsa->P, kdata, key_size/2);
|
|
mbedtls_mpi_write_binary(&rsa->Q, kdata+key_size/2, key_size/2);
|
|
}
|
|
else {
|
|
mbedtls_ecdsa_context *ecdsa = (mbedtls_ecdsa_context *)key_ctx;
|
|
key_size = mbedtls_mpi_size(&ecdsa->d);
|
|
kdata = (uint8_t *)calloc(1, key_size+1);
|
|
kdata[0] = ecdsa->grp.id & 0xff;
|
|
mbedtls_mpi_write_binary(&ecdsa->d, kdata+1, key_size);
|
|
key_size++;
|
|
}
|
|
if ((r = load_dkek()) != HSM_OK)
|
|
return r;
|
|
if ((r = encrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size)) != 0)
|
|
return r;
|
|
release_dkek();
|
|
file_t *fpk = file_new((KEY_PREFIX << 8) | key_id);
|
|
r = flash_write_data_to_file(fpk, kdata, key_size);
|
|
free(kdata);
|
|
if (r != HSM_OK)
|
|
return r;
|
|
//add_file_to_chain(fpk, &ef_kf);
|
|
|
|
struct sc_pkcs15_object *p15o = (struct sc_pkcs15_object *)calloc(1,sizeof (struct sc_pkcs15_object));
|
|
|
|
sc_pkcs15_prkey_info_t *prkd = (sc_pkcs15_prkey_info_t *)calloc(1, sizeof (sc_pkcs15_prkey_info_t));
|
|
memset(prkd, 0, sizeof(sc_pkcs15_prkey_info_t));
|
|
prkd->id.len = 1;
|
|
prkd->id.value[0] = key_id;
|
|
prkd->usage = SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_UNWRAP;
|
|
prkd->access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE | SC_PKCS15_PRKEY_ACCESS_LOCAL;
|
|
prkd->native = 1;
|
|
prkd->key_reference = key_id;
|
|
prkd->path.value[0] = PRKD_PREFIX;
|
|
prkd->path.value[1] = key_id;
|
|
prkd->path.len = 2;
|
|
if (type == SC_PKCS15_TYPE_PRKEY_RSA)
|
|
prkd->modulus_length = key_size;
|
|
else
|
|
prkd->field_length = key_size-1; //contains 1 byte for the grp id
|
|
|
|
p15o->data = prkd;
|
|
p15o->type = SC_PKCS15_TYPE_PRKEY | (type & 0xff);
|
|
|
|
r = sc_pkcs15_encode_prkdf_entry(ctx, p15o, &asn1bin, &asn1len);
|
|
free(prkd);
|
|
//sc_asn1_print_tags(asn1bin, asn1len);
|
|
fpk = file_new((PRKD_PREFIX << 8) | key_id);
|
|
r = flash_write_data_to_file(fpk, asn1bin, asn1len);
|
|
free(asn1bin);
|
|
if (r != HSM_OK)
|
|
return r;
|
|
//add_file_to_chain(fpk, &ef_prkdf);
|
|
/*
|
|
sc_pkcs15_pubkey_info_t *pukd = (sc_pkcs15_pubkey_info_t *)calloc(1, sizeof(sc_pkcs15_pubkey_info_t));
|
|
memset(pukd, 0, sizeof(sc_pkcs15_pubkey_info_t));
|
|
pukd->id.len = 1;
|
|
pukd->id.value[0] = key_id;
|
|
pukd->usage = SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_VERIFY;
|
|
pukd->access_flags = SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE;
|
|
pukd->native = 1;
|
|
pukd->key_reference = key_id;
|
|
pukd->path.value[0] = CD_PREFIX;
|
|
pukd->path.value[1] = key_id;
|
|
pukd->path.len = 2;
|
|
|
|
if (type == SC_PKCS15_TYPE_PRKEY_RSA)
|
|
pukd->modulus_length = key_size;
|
|
else
|
|
pukd->field_length = key_size-1;
|
|
|
|
p15o->data = pukd;
|
|
p15o->type = SC_PKCS15_TYPE_PUBKEY | (type & 0xff);
|
|
|
|
r = sc_pkcs15_encode_pukdf_entry(ctx, p15o, &asn1bin, &asn1len);
|
|
free(pukd);
|
|
free(p15o);
|
|
//sc_asn1_print_tags(asn1bin, asn1len);
|
|
fpk = file_new((EE_CERTIFICATE_PREFIX << 8) | key_id);
|
|
r = flash_write_data_to_file(fpk, asn1bin, asn1len);
|
|
free(asn1bin);
|
|
if (r != HSM_OK)
|
|
return r;
|
|
//add_file_to_chain(fpk, &ef_cdf);
|
|
*/
|
|
low_flash_available();
|
|
return HSM_OK;
|
|
}
|
|
|
|
sc_context_t *create_context() {
|
|
sc_context_t *ctx;
|
|
sc_context_param_t ctx_opts;
|
|
memset(&ctx_opts, 0, sizeof(sc_context_param_t));
|
|
ctx_opts.ver = 0;
|
|
ctx_opts.app_name = "hsm2040";
|
|
sc_context_create(&ctx, &ctx_opts);
|
|
ctx->debug = 0;
|
|
sc_ctx_log_to_file(ctx, "stdout");
|
|
return ctx;
|
|
}
|
|
|
|
void cvc_init_common(sc_cvc_t *cvc, sc_context_t *ctx) {
|
|
memset(cvc, 0, sizeof(sc_cvc_t));
|
|
|
|
size_t lencar = 0, lenchr = 0;
|
|
const unsigned char *car = sc_asn1_find_tag(ctx, (const uint8_t *)apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, 0x42, &lencar);
|
|
const unsigned char *chr = sc_asn1_find_tag(ctx, (const uint8_t *)apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, 0x5f20, &lenchr);
|
|
if (car && lencar > 0)
|
|
strlcpy(cvc->car, car, MIN(lencar,sizeof(cvc->car)));
|
|
else
|
|
strlcpy(cvc->car, "UTCA00001", sizeof(cvc->car));
|
|
if (chr && lenchr > 0)
|
|
strlcpy(cvc->chr, chr, MIN(lenchr, sizeof(cvc->chr)));
|
|
else
|
|
strlcpy(cvc->chr, "ESHSMCVCA00001", sizeof(cvc->chr));
|
|
strlcpy(cvc->outer_car, "ESHSM00001", sizeof(cvc->outer_car));
|
|
}
|
|
|
|
int cvc_prepare_signatures(sc_pkcs15_card_t *p15card, sc_cvc_t *cvc, size_t sig_len, uint8_t *hsh) {
|
|
uint8_t *cvcbin;
|
|
size_t cvclen;
|
|
cvc->signatureLen = sig_len;
|
|
cvc->signature = (uint8_t *)calloc(1, sig_len);
|
|
cvc->outerSignatureLen = 4;
|
|
cvc->outerSignature = (uint8_t *)calloc(1, sig_len);
|
|
int r = sc_pkcs15emu_sc_hsm_encode_cvc(p15card, cvc, &cvcbin, &cvclen);
|
|
if (r != SC_SUCCESS) {
|
|
if (cvcbin)
|
|
free(cvcbin);
|
|
return r;
|
|
}
|
|
hash(cvcbin, cvclen, hsh);
|
|
free(cvcbin);
|
|
return HSM_OK;
|
|
}
|
|
|
|
static int cmd_keypair_gen() {
|
|
uint8_t key_id = P1(apdu);
|
|
uint8_t auth_key_id = P2(apdu);
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
sc_context_t *ctx = create_context();
|
|
struct sc_pkcs15_card p15card;
|
|
p15card.card = (sc_card_t *)calloc(1, sizeof(sc_card_t));
|
|
p15card.card->ctx = ctx;
|
|
int ret = 0;
|
|
sc_cvc_t cvc;
|
|
cvc_init_common(&cvc, ctx);
|
|
|
|
size_t tout = 0;
|
|
//sc_asn1_print_tags(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
|
|
const uint8_t *p = sc_asn1_find_tag(ctx, (const uint8_t *)apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, 0x7f49, &tout);
|
|
if (p) {
|
|
size_t oid_len = 0;
|
|
const uint8_t *oid = sc_asn1_find_tag(ctx, p, tout, 0x6, &oid_len);
|
|
if (oid) {
|
|
if (memcmp(oid, "\x4\x0\x7F\x0\x7\x2\x2\x2\x1\x2",MIN(oid_len,10)) == 0) { //RSA
|
|
size_t ex_len, ks_len;
|
|
const uint8_t *ex = sc_asn1_find_tag(ctx, p, tout, 0x82, &ex_len);
|
|
const uint8_t *ks = sc_asn1_find_tag(ctx, p, tout, 0x2, &ks_len);
|
|
int exponent = 65537, key_size = 2048;
|
|
if (ex) {
|
|
sc_asn1_decode_integer(ex, ex_len, &exponent, 0);
|
|
}
|
|
if (ks) {
|
|
sc_asn1_decode_integer(ks, ks_len, &key_size, 0);
|
|
}
|
|
printf("KEYPAIR RSA %d\r\n",key_size);
|
|
mbedtls_rsa_context rsa;
|
|
mbedtls_rsa_init(&rsa);
|
|
uint8_t index = 0;
|
|
ret = mbedtls_rsa_gen_key(&rsa, random_gen, &index, key_size, exponent);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_rsa_free(&rsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
struct sc_object_id rsa15withSHA256 = { { 0,4,0,127,0,7,2,2,2,1,2,-1 } };
|
|
cvc.coefficientAorExponentlen = ex_len;
|
|
cvc.coefficientAorExponent = calloc(1, cvc.coefficientAorExponentlen);
|
|
memcpy(cvc.coefficientAorExponent, &exponent, cvc.coefficientAorExponentlen);
|
|
|
|
cvc.pukoid = rsa15withSHA256;
|
|
//cvc.modulusSize = key_size; //NOT EXPECTED. DO NOT COMMENT (it seems not standard)
|
|
cvc.primeOrModuluslen = key_size/8;
|
|
cvc.primeOrModulus = (uint8_t *)calloc(1, cvc.primeOrModuluslen);
|
|
ret = mbedtls_mpi_write_binary(&rsa.N, cvc.primeOrModulus, cvc.primeOrModuluslen);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_rsa_free(&rsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
uint8_t hsh[32];
|
|
ret = cvc_prepare_signatures(&p15card, &cvc, key_size/8, hsh);
|
|
if (ret != HSM_OK) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_rsa_free(&rsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
ret = mbedtls_rsa_rsassa_pkcs1_v15_sign(&rsa, random_gen, &index, MBEDTLS_MD_SHA256, 32, hsh, cvc.signature);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_rsa_free(&rsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
ret = store_keys(&rsa, SC_PKCS15_TYPE_PRKEY_RSA, key_id, ctx);
|
|
if (ret != HSM_OK) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_rsa_free(&rsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
mbedtls_rsa_free(&rsa);
|
|
}
|
|
else if (memcmp(oid, "\x4\x0\x7F\x0\x7\x2\x2\x2\x2\x3",MIN(oid_len,10)) == 0) { //ECC
|
|
size_t prime_len;
|
|
const uint8_t *prime = sc_asn1_find_tag(ctx, p, tout, 0x81, &prime_len);
|
|
mbedtls_ecp_group_id ec_id = MBEDTLS_ECP_DP_NONE;
|
|
for (struct ec_curve_mbed_id *ec = ec_curves_mbed; ec->id != MBEDTLS_ECP_DP_NONE; ec++) {
|
|
if (prime_len == ec->curve.len && memcmp(prime, ec->curve.value, prime_len) == 0) {
|
|
ec_id = ec->id;
|
|
break;
|
|
}
|
|
}
|
|
printf("KEYPAIR ECC %d\r\n",ec_id);
|
|
if (ec_id == MBEDTLS_ECP_DP_NONE) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_FUNC_NOT_SUPPORTED();
|
|
}
|
|
mbedtls_ecdsa_context ecdsa;
|
|
mbedtls_ecdsa_init(&ecdsa);
|
|
uint8_t index = 0;
|
|
ret = mbedtls_ecdsa_genkey(&ecdsa, ec_id, random_gen, &index);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
struct sc_object_id ecdsaWithSHA256 = { { 0,4,0,127,0,7,2,2,2,2,3,-1 } };
|
|
cvc.pukoid = ecdsaWithSHA256;
|
|
|
|
cvc.coefficientAorExponentlen = prime_len;//mbedtls_mpi_size(&ecdsa.grp.A);
|
|
cvc.coefficientAorExponent = calloc(1, cvc.coefficientAorExponentlen);
|
|
ret = mbedtls_mpi_write_binary(&ecdsa.grp.A, cvc.coefficientAorExponent, cvc.coefficientAorExponentlen);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
cvc.primeOrModuluslen = mbedtls_mpi_size(&ecdsa.grp.P);
|
|
cvc.primeOrModulus = (uint8_t *)calloc(1, cvc.primeOrModuluslen);
|
|
ret = mbedtls_mpi_write_binary(&ecdsa.grp.P, cvc.primeOrModulus, cvc.primeOrModuluslen);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
cvc.coefficientBlen = mbedtls_mpi_size(&ecdsa.grp.B);
|
|
cvc.coefficientB = (uint8_t *)calloc(1, cvc.coefficientBlen);
|
|
ret = mbedtls_mpi_write_binary(&ecdsa.grp.B, cvc.coefficientB, cvc.coefficientBlen);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
cvc.basePointGlen = mbedtls_mpi_size(&ecdsa.grp.G.X)+mbedtls_mpi_size(&ecdsa.grp.G.Y)+mbedtls_mpi_size(&ecdsa.grp.G.Z);
|
|
cvc.basePointG = (uint8_t *)calloc(1, cvc.basePointGlen);
|
|
ret = mbedtls_ecp_point_write_binary(&ecdsa.grp, &ecdsa.grp.G, MBEDTLS_ECP_PF_UNCOMPRESSED, &cvc.basePointGlen, cvc.basePointG, cvc.basePointGlen);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
cvc.orderlen = mbedtls_mpi_size(&ecdsa.grp.N);
|
|
cvc.order = (uint8_t *)calloc(1, cvc.orderlen);
|
|
ret = mbedtls_mpi_write_binary(&ecdsa.grp.N, cvc.order, cvc.orderlen);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
cvc.publicPointlen = mbedtls_mpi_size(&ecdsa.Q.X)+mbedtls_mpi_size(&ecdsa.Q.Y)+mbedtls_mpi_size(&ecdsa.Q.Z);
|
|
cvc.publicPoint = (uint8_t *)calloc(1, cvc.publicPointlen);
|
|
ret = mbedtls_ecp_point_write_binary(&ecdsa.grp, &ecdsa.Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &cvc.publicPointlen, cvc.publicPoint, cvc.publicPointlen);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
cvc.cofactorlen = 1;
|
|
cvc.cofactor = (uint8_t *)calloc(1, cvc.cofactorlen);
|
|
cvc.cofactor[0] = 1;
|
|
|
|
cvc.modulusSize = ec_id; //we store the ec_id in the modulusSize, used for RSA, as it is an integer
|
|
|
|
uint8_t hsh[32];
|
|
ret = cvc_prepare_signatures(&p15card, &cvc, ecdsa.grp.pbits*2/8+9, hsh);
|
|
if (ret != HSM_OK) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
ret = mbedtls_ecdsa_write_signature(&ecdsa, MBEDTLS_MD_SHA256, hsh, sizeof(hsh), cvc.signature, cvc.signatureLen, &cvc.signatureLen, random_gen, &index);
|
|
if (ret != 0) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
ret = store_keys(&ecdsa, SC_PKCS15_TYPE_PRKEY_EC, key_id, ctx);
|
|
if (ret != HSM_OK) {
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
mbedtls_ecdsa_free(&ecdsa);
|
|
}
|
|
|
|
}
|
|
}
|
|
uint8_t *cvcbin;
|
|
size_t cvclen;
|
|
ret = sc_pkcs15emu_sc_hsm_encode_cvc(&p15card, &cvc, &cvcbin, &cvclen);
|
|
sc_pkcs15emu_sc_hsm_free_cvc(&cvc);
|
|
free(ctx);
|
|
free(p15card.card);
|
|
if (ret != SC_SUCCESS) {
|
|
if (cvcbin)
|
|
free(cvcbin);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
|
|
res_APDU[0] = 0x67;
|
|
int outer_len = strlen(cvc.outer_car)+2+2+1+4;
|
|
int bytes_length = (cvclen+outer_len)/256;
|
|
if (cvclen%256 > 0)
|
|
bytes_length++;
|
|
if (cvclen < 128)
|
|
res_APDU[1] = cvclen+outer_len;
|
|
else {
|
|
res_APDU[1] = 0x80|bytes_length;
|
|
for (int b = 1; b <= bytes_length; b++)
|
|
res_APDU[1+b] = ((cvclen+outer_len)>>((bytes_length-b)*8))&0xff;
|
|
}
|
|
memcpy(res_APDU+bytes_length+2, cvcbin, cvclen);
|
|
res_APDU[bytes_length+2+cvclen] = 0x42;
|
|
res_APDU[bytes_length+2+cvclen+1] = strlen(cvc.outer_car);
|
|
memcpy(res_APDU+bytes_length+2+cvclen+2, cvc.outer_car, strlen(cvc.outer_car));
|
|
memcpy(res_APDU+bytes_length+2+cvclen+2+strlen(cvc.outer_car), "\x5F\x37\x04",3);
|
|
free(cvcbin);
|
|
res_APDU_size = cvclen+bytes_length+2+outer_len;
|
|
apdu.expected_res_size = res_APDU_size;
|
|
|
|
//sc_asn1_print_tags(res_APDU, res_APDU_size);
|
|
file_t *fpk = file_new((EE_CERTIFICATE_PREFIX << 8) | key_id);
|
|
ret = flash_write_data_to_file(fpk, res_APDU, res_APDU_size);
|
|
if (ret != 0)
|
|
return SW_EXEC_ERROR();
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_update_ef() {
|
|
uint8_t p1 = P1(apdu), p2 = P2(apdu);
|
|
uint16_t fid = (p1 << 8) | p2;
|
|
uint8_t *p = apdu.cmd_apdu_data, *data;
|
|
uint16_t offset = 0;
|
|
uint16_t data_len = 0;
|
|
file_t *ef = NULL;
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
if (fid == 0x0)
|
|
ef = currentEF;
|
|
else if (p1 != EE_CERTIFICATE_PREFIX && p1 != PRKD_PREFIX && p1 != CA_CERTIFICATE_PREFIX && p1 != CD_PREFIX)
|
|
return SW_INCORRECT_P1P2();
|
|
|
|
if (ef && !authenticate_action(ef, ACL_OP_UPDATE_ERASE))
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
|
|
while (p-apdu.cmd_apdu_data < apdu.cmd_apdu_data_len) {
|
|
uint8_t tag = *p++;
|
|
uint8_t taglen = *p++;
|
|
if (tag == 0x54) { //ofset tag
|
|
for (int i = 0; i < taglen; i++)
|
|
offset |= (*p++ << (8*(taglen-i-1)));
|
|
}
|
|
else if (tag == 0x53) { //data
|
|
if (taglen == 0x82) {
|
|
data_len = *p++ << 8;
|
|
data_len |= *p++;
|
|
}
|
|
else if (taglen == 0x81) {
|
|
data_len = *p++;
|
|
}
|
|
else
|
|
data_len = taglen;
|
|
data = p;
|
|
p += data_len;
|
|
}
|
|
}
|
|
if (data_len == 0 && offset == 0) { //new file
|
|
ef = file_new(fid);
|
|
//if ((fid & 0xff00) == (EE_CERTIFICATE_PREFIX << 8))
|
|
// add_file_to_chain(ef, &ef_pukdf);
|
|
select_file(ef);
|
|
}
|
|
else {
|
|
if (fid == 0x0 && !ef)
|
|
return SW_FILE_NOT_FOUND();
|
|
else if (fid != 0x0 && !(ef = search_by_fid(fid, NULL, SPECIFY_EF)) && !(ef = search_dynamic_file(fid))) { //if does not exist, create it
|
|
//return SW_FILE_NOT_FOUND();
|
|
ef = file_new(fid);
|
|
}
|
|
if (offset == 0) {
|
|
int r = flash_write_data_to_file(ef, data, data_len);
|
|
if (r != HSM_OK)
|
|
return SW_MEMORY_FAILURE();
|
|
}
|
|
else {
|
|
if (!ef->data)
|
|
return SW_DATA_INVALID();
|
|
uint8_t *data_merge = (uint8_t *)calloc(1, offset+data_len);
|
|
memcpy(data_merge, file_read(ef->data), offset);
|
|
memcpy(data_merge+offset, data, data_len);
|
|
int r = flash_write_data_to_file(ef, data_merge, data_len);
|
|
free(data_merge);
|
|
if (r != HSM_OK)
|
|
return SW_MEMORY_FAILURE();
|
|
}
|
|
low_flash_available();
|
|
}
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_delete_file() {
|
|
file_t *ef = NULL;
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
|
|
if (apdu.cmd_apdu_data_len == 0) {
|
|
ef = currentEF;
|
|
if (!(ef = search_dynamic_file(ef->fid)))
|
|
return SW_FILE_INVALID();
|
|
}
|
|
else {
|
|
uint16_t fid = (apdu.cmd_apdu_data[0] << 8) | apdu.cmd_apdu_data[1];
|
|
if (!(ef = search_dynamic_file(fid)))
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
if (!authenticate_action(ef, ACL_OP_DELETE_SELF))
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
if (flash_clear_file(ef) != HSM_OK)
|
|
return SW_EXEC_ERROR();
|
|
if (delete_dynamic_file(ef) != HSM_OK)
|
|
return SW_EXEC_ERROR();
|
|
low_flash_available();
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_change_pin() {
|
|
if (P1(apdu) == 0x0) {
|
|
if (P2(apdu) == 0x81) {
|
|
if (!file_sopin || !file_pin1) {
|
|
return SW_FILE_NOT_FOUND();
|
|
}
|
|
if (!file_pin1->data) {
|
|
return SW_REFERENCE_NOT_FOUND();
|
|
}
|
|
uint8_t pin_len = file_read_uint8(file_pin1->data+2);
|
|
uint16_t r = check_pin(file_pin1, apdu.cmd_apdu_data, pin_len);
|
|
if (r != 0x9000)
|
|
return r;
|
|
if (load_dkek() != HSM_OK) //loads the DKEK with old pin
|
|
return SW_EXEC_ERROR();
|
|
//encrypt DKEK with new pin
|
|
hash_multi(apdu.cmd_apdu_data+pin_len, apdu.cmd_apdu_data_len-pin_len, session_pin);
|
|
has_session_pin = true;
|
|
encrypt(session_pin, tmp_dkek, tmp_dkek+IV_SIZE, 32);
|
|
file_t *tf = search_by_fid(EF_DKEK, NULL, SPECIFY_EF);
|
|
flash_write_data_to_file(tf, tmp_dkek, sizeof(tmp_dkek));
|
|
release_dkek();
|
|
uint8_t dhash[33];
|
|
dhash[0] = apdu.cmd_apdu_data_len-pin_len;
|
|
double_hash_pin(apdu.cmd_apdu_data+pin_len, apdu.cmd_apdu_data_len-pin_len, dhash+1);
|
|
flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
|
|
low_flash_available();
|
|
return SW_OK();
|
|
}
|
|
}
|
|
}
|
|
|
|
static int cmd_key_gen() {
|
|
uint8_t key_id = P1(apdu);
|
|
uint8_t p2 = P2(apdu);
|
|
uint8_t key_size = 32;
|
|
int r;
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
if (p2 == 0xB2)
|
|
key_size = 32;
|
|
else if (p2 == 0xB1)
|
|
key_size = 24;
|
|
else if (p2 == 0xB0)
|
|
key_size = 16;
|
|
//at this moment, we do not use the template, as only CBC is supported by the driver (encrypt, decrypt and CMAC)
|
|
uint8_t aes_key[32]; //maximum AES key size
|
|
memcpy(aes_key, random_bytes_get(key_size), key_size);
|
|
if ((r = load_dkek()) != HSM_OK)
|
|
return SW_EXEC_ERROR() ;
|
|
if ((r = encrypt(tmp_dkek+IV_SIZE, tmp_dkek, aes_key, key_size)) != 0)
|
|
return SW_EXEC_ERROR() ;
|
|
release_dkek();
|
|
file_t *fpk = file_new((KEY_PREFIX << 8) | key_id);
|
|
if (!fpk)
|
|
return SW_MEMORY_FAILURE();
|
|
r = flash_write_data_to_file(fpk, aes_key, key_size);
|
|
if (r != HSM_OK)
|
|
return SW_MEMORY_FAILURE();
|
|
fpk = file_new((PRKD_PREFIX << 8) | key_id);
|
|
if (!fpk)
|
|
return SW_MEMORY_FAILURE();
|
|
r = flash_write_data_to_file(fpk, NULL, 0);
|
|
if (r != HSM_OK)
|
|
return SW_MEMORY_FAILURE();
|
|
low_flash_available();
|
|
return SW_OK();
|
|
}
|
|
|
|
int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
|
|
int key_size = file_read_uint16(fkey->data);
|
|
if (load_dkek() != HSM_OK)
|
|
return SW_EXEC_ERROR();
|
|
uint8_t *kdata = (uint8_t *)calloc(1,key_size);
|
|
memcpy(kdata, file_read(fkey->data+2), key_size);
|
|
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0) {
|
|
free(kdata);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
release_dkek();
|
|
if (mbedtls_mpi_read_binary(&ctx->P, kdata, key_size/2) != 0) {
|
|
mbedtls_rsa_free(ctx);
|
|
free(kdata);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
if (mbedtls_mpi_read_binary(&ctx->Q, kdata+key_size/2, key_size/2) != 0) {
|
|
mbedtls_rsa_free(ctx);
|
|
free(kdata);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
free(kdata);
|
|
if (mbedtls_mpi_lset(&ctx->E, 0x10001) != 0) {
|
|
mbedtls_rsa_free(ctx);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
if (mbedtls_rsa_import(ctx, NULL, &ctx->P, &ctx->Q, NULL, &ctx->E) != 0) {
|
|
mbedtls_rsa_free(ctx);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
if (mbedtls_rsa_complete(ctx) != 0) {
|
|
mbedtls_rsa_free(ctx);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
if (mbedtls_rsa_check_privkey(ctx) != 0) {
|
|
mbedtls_rsa_free(ctx);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
return HSM_OK;
|
|
}
|
|
|
|
int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) {
|
|
int key_size = file_read_uint16(fkey->data);
|
|
if (load_dkek() != HSM_OK)
|
|
return HSM_EXEC_ERROR;
|
|
uint8_t *kdata = (uint8_t *)calloc(1,key_size);
|
|
memcpy(kdata, file_read(fkey->data+2), key_size);
|
|
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0) {
|
|
free(kdata);
|
|
return HSM_EXEC_ERROR;
|
|
}
|
|
release_dkek();
|
|
mbedtls_ecp_group_id gid = kdata[0];
|
|
int r = mbedtls_ecp_read_key(gid, ctx, kdata+1, key_size-1);
|
|
if (r != 0) {
|
|
free(kdata);
|
|
mbedtls_ecdsa_free(ctx);
|
|
return HSM_EXEC_ERROR;
|
|
}
|
|
free(kdata);
|
|
return HSM_OK;
|
|
}
|
|
|
|
static int cmd_signature() {
|
|
uint8_t key_id = P1(apdu);
|
|
uint8_t p2 = P2(apdu);
|
|
mbedtls_md_type_t md = MBEDTLS_MD_NONE;
|
|
file_t *fkey;
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
if (!(fkey = search_dynamic_file((KEY_PREFIX << 8) | key_id)) || !fkey->data)
|
|
return SW_FILE_NOT_FOUND();
|
|
int key_size = file_read_uint16(fkey->data);
|
|
if (p2 == ALGO_RSA_PKCS1_SHA1 || p2 == ALGO_RSA_PSS_SHA1 || p2 == ALGO_EC_SHA1)
|
|
md = MBEDTLS_MD_SHA1;
|
|
else if (p2 == ALGO_RSA_PKCS1_SHA256 || p2 == ALGO_RSA_PSS_SHA256 || p2 == ALGO_EC_SHA256)
|
|
md = MBEDTLS_MD_SHA256;
|
|
else if (p2 == ALGO_EC_SHA224)
|
|
md = MBEDTLS_MD_SHA224;
|
|
if (p2 == ALGO_RSA_PKCS1_SHA1 || p2 == ALGO_RSA_PSS_SHA1 || p2 == ALGO_EC_SHA1 || p2 == ALGO_RSA_PKCS1_SHA256 || p2 == ALGO_RSA_PSS_SHA256 || p2 == ALGO_EC_SHA256 || p2 == ALGO_EC_SHA224) {
|
|
generic_hash(md, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, apdu.cmd_apdu_data);
|
|
apdu.cmd_apdu_data_len = mbedtls_md_get_size(mbedtls_md_info_from_type(md));
|
|
}
|
|
if (p2 == ALGO_RSA_RAW || p2 == ALGO_RSA_PKCS1 || p2 == ALGO_RSA_PKCS1_SHA1 || p2 == ALGO_RSA_PKCS1_SHA256 || p2 == ALGO_RSA_PSS || p2 == ALGO_RSA_PSS_SHA1 || p2 == ALGO_RSA_PSS_SHA256) {
|
|
mbedtls_rsa_context ctx;
|
|
mbedtls_rsa_init(&ctx);
|
|
|
|
int r;
|
|
r = load_private_key_rsa(&ctx, fkey);
|
|
if (r != HSM_OK)
|
|
return SW_EXEC_ERROR();
|
|
const uint8_t *hash = apdu.cmd_apdu_data;
|
|
size_t hash_len = apdu.cmd_apdu_data_len;
|
|
if (p2 == ALGO_RSA_PKCS1) { //DigestInfo attached
|
|
unsigned int algo;
|
|
if (sc_pkcs1_strip_digest_info_prefix(&algo, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, apdu.cmd_apdu_data, &apdu.cmd_apdu_data_len) != SC_SUCCESS) //gets the MD algo id and strips it off
|
|
return SW_EXEC_ERROR();
|
|
if (algo == SC_ALGORITHM_RSA_HASH_SHA1)
|
|
md = MBEDTLS_MD_SHA1;
|
|
else if (algo == SC_ALGORITHM_RSA_HASH_SHA224)
|
|
md = MBEDTLS_MD_SHA224;
|
|
else if (algo == SC_ALGORITHM_RSA_HASH_SHA256)
|
|
md = MBEDTLS_MD_SHA256;
|
|
else if (algo == SC_ALGORITHM_RSA_HASH_SHA384)
|
|
md = MBEDTLS_MD_SHA384;
|
|
else if (algo == SC_ALGORITHM_RSA_HASH_SHA512)
|
|
md = MBEDTLS_MD_SHA512;
|
|
}
|
|
else {
|
|
//sc_asn1_print_tags(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
|
|
size_t tout = 0, oid_len = 0;
|
|
const uint8_t *p = sc_asn1_find_tag(NULL, (const uint8_t *)apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, 0x30, &tout), *oid = NULL;
|
|
if (p) {
|
|
size_t tout30 = 0;
|
|
const uint8_t *c30 = sc_asn1_find_tag(NULL, p, tout, 0x30, &tout30);
|
|
if (c30) {
|
|
oid = sc_asn1_find_tag(NULL, c30, tout30, 0x6, &oid_len);
|
|
}
|
|
hash = sc_asn1_find_tag(NULL, p, tout, 0x4, &hash_len);
|
|
}
|
|
if (oid && oid_len > 0) {
|
|
if (memcmp(oid, "\x2B\x0E\x03\x02\x1A", oid_len) == 0)
|
|
md = MBEDTLS_MD_SHA1;
|
|
else if (memcmp(oid, "\x60\x86\x48\x01\x65\x03\x04\x02\x04", oid_len) == 0)
|
|
md = MBEDTLS_MD_SHA224;
|
|
else if (memcmp(oid, "\x60\x86\x48\x01\x65\x03\x04\x02\x01", oid_len) == 0)
|
|
md = MBEDTLS_MD_SHA256;
|
|
else if (memcmp(oid, "\x60\x86\x48\x01\x65\x03\x04\x02\x02", oid_len) == 0)
|
|
md = MBEDTLS_MD_SHA384;
|
|
else if (memcmp(oid, "\x60\x86\x48\x01\x65\x03\x04\x02\x03", oid_len) == 0)
|
|
md = MBEDTLS_MD_SHA512;
|
|
}
|
|
if (p2 == ALGO_RSA_PSS || p2 == ALGO_RSA_PSS_SHA1 || p2 == ALGO_RSA_PSS_SHA256) {
|
|
if (p2 == ALGO_RSA_PSS && !oid) {
|
|
if (apdu.cmd_apdu_data_len == 20) //default is sha1
|
|
md = MBEDTLS_MD_SHA1;
|
|
else if (apdu.cmd_apdu_data_len == 32)
|
|
md = MBEDTLS_MD_SHA256;
|
|
}
|
|
mbedtls_rsa_set_padding(&ctx, MBEDTLS_RSA_PKCS_V21, md);
|
|
}
|
|
}
|
|
if (md == MBEDTLS_MD_NONE) {
|
|
if (apdu.cmd_apdu_data_len < key_size) //needs padding
|
|
memset(apdu.cmd_apdu_data+apdu.cmd_apdu_data_len, 0, key_size-apdu.cmd_apdu_data_len);
|
|
r = mbedtls_rsa_private(&ctx, random_gen, NULL, apdu.cmd_apdu_data, res_APDU);
|
|
}
|
|
else {
|
|
uint8_t *signature = (uint8_t *)calloc(key_size, sizeof(uint8_t));
|
|
r = mbedtls_rsa_pkcs1_sign(&ctx, random_gen, NULL, md, hash_len, hash, signature);
|
|
memcpy(res_APDU, signature, key_size);
|
|
free(signature);
|
|
}
|
|
if (r != 0) {
|
|
mbedtls_rsa_free(&ctx);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
res_APDU_size = key_size;
|
|
apdu.expected_res_size = key_size;
|
|
mbedtls_rsa_free(&ctx);
|
|
}
|
|
else if (p2 == ALGO_EC_RAW || p2 == ALGO_EC_SHA1 || p2 == ALGO_EC_SHA224 || p2 == ALGO_EC_SHA256) {
|
|
mbedtls_ecdsa_context ctx;
|
|
mbedtls_ecdsa_init(&ctx);
|
|
md = MBEDTLS_MD_SHA256;
|
|
if (p2 == ALGO_EC_RAW) {
|
|
if (apdu.cmd_apdu_data_len == 32)
|
|
md = MBEDTLS_MD_SHA256;
|
|
else if (apdu.cmd_apdu_data_len == 20)
|
|
md = MBEDTLS_MD_SHA1;
|
|
else if (apdu.cmd_apdu_data_len == 28)
|
|
md = MBEDTLS_MD_SHA224;
|
|
else if (apdu.cmd_apdu_data_len == 48)
|
|
md = MBEDTLS_MD_SHA384;
|
|
else if (apdu.cmd_apdu_data_len == 64)
|
|
md = MBEDTLS_MD_SHA512;
|
|
}
|
|
if (p2 == ALGO_EC_SHA1)
|
|
md = MBEDTLS_MD_SHA1;
|
|
else if (p2 == ALGO_EC_SHA224)
|
|
md = MBEDTLS_MD_SHA224;
|
|
else if (p2 == ALGO_EC_SHA256)
|
|
md = MBEDTLS_MD_SHA256;
|
|
int r;
|
|
r = load_private_key_ecdsa(&ctx, fkey);
|
|
if (r != HSM_OK)
|
|
return SW_CONDITIONS_NOT_SATISFIED();
|
|
size_t olen = 0;
|
|
uint8_t buf[MBEDTLS_ECDSA_MAX_LEN];
|
|
if (mbedtls_ecdsa_write_signature(&ctx, md, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, buf, MBEDTLS_ECDSA_MAX_LEN, &olen, random_gen, NULL) != 0) {
|
|
mbedtls_ecdsa_free(&ctx);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
memcpy(res_APDU, buf, olen);
|
|
res_APDU_size = olen;
|
|
mbedtls_ecdsa_free(&ctx);
|
|
}
|
|
else
|
|
return SW_INCORRECT_P1P2();
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_key_wrap() {
|
|
int key_id = P1(apdu);
|
|
if (P2(apdu) != 0x92)
|
|
return SW_WRONG_P1P2();
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
file_t *ef = search_dynamic_file((KEY_PREFIX << 8) | key_id);
|
|
if (!ef)
|
|
return SW_FILE_NOT_FOUND();
|
|
int key_len = file_read_uint16(ef->data);
|
|
memcpy(res_APDU, file_read(ef->data+2), key_len);
|
|
res_APDU_size = key_len;
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_key_unwrap() {
|
|
int key_id = P1(apdu);
|
|
if (P2(apdu) != 0x93)
|
|
return SW_WRONG_P1P2();
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
file_t *ef = search_dynamic_file((KEY_PREFIX << 8) | key_id);
|
|
if (!ef)
|
|
ef = file_new((KEY_PREFIX << 8) | key_id);
|
|
flash_write_data_to_file(ef, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len);
|
|
low_flash_available();
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_decrypt_asym() {
|
|
int key_id = P1(apdu);
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
file_t *ef = search_dynamic_file((KEY_PREFIX << 8) | key_id);
|
|
if (!ef)
|
|
return SW_FILE_NOT_FOUND();
|
|
if (P2(apdu) == ALGO_RSA_DECRYPT) {
|
|
mbedtls_rsa_context ctx;
|
|
mbedtls_rsa_init(&ctx);
|
|
int r = load_private_key_rsa(&ctx, ef);
|
|
if (r != HSM_OK)
|
|
return SW_EXEC_ERROR();
|
|
int key_size = file_read_uint16(ef->data);
|
|
if (apdu.cmd_apdu_data_len < key_size) //needs padding
|
|
memset(apdu.cmd_apdu_data+apdu.cmd_apdu_data_len, 0, key_size-apdu.cmd_apdu_data_len);
|
|
r = mbedtls_rsa_private(&ctx, random_gen, NULL, apdu.cmd_apdu_data, res_APDU);
|
|
if (r != 0) {
|
|
mbedtls_rsa_free(&ctx);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
res_APDU_size = key_size;
|
|
mbedtls_rsa_free(&ctx);
|
|
}
|
|
else if (P2(apdu) == ALGO_EC_DH) {
|
|
mbedtls_ecdh_context ctx;
|
|
int key_size = file_read_uint16(ef->data);
|
|
if (load_dkek() != HSM_OK)
|
|
return SW_EXEC_ERROR();
|
|
uint8_t *kdata = (uint8_t *)calloc(1,key_size);
|
|
memcpy(kdata, file_read(ef->data+2), key_size);
|
|
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0) {
|
|
free(kdata);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
release_dkek();
|
|
mbedtls_ecdh_init(&ctx);
|
|
mbedtls_ecp_group_id gid = kdata[0];
|
|
int r = 0;
|
|
r = mbedtls_ecdh_setup(&ctx, gid);
|
|
if (r != 0) {
|
|
mbedtls_ecdh_free(&ctx);
|
|
free(kdata);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
r = mbedtls_mpi_read_binary(&ctx.ctx.mbed_ecdh.d, kdata+1, key_size-1);
|
|
if (r != 0) {
|
|
mbedtls_ecdh_free(&ctx);
|
|
free(kdata);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
free(kdata);
|
|
r = mbedtls_ecdh_read_public(&ctx, apdu.cmd_apdu_data-1, apdu.cmd_apdu_data_len+1);
|
|
if (r != 0) {
|
|
mbedtls_ecdh_free(&ctx);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
size_t olen = 0;
|
|
res_APDU[0] = 0x04;
|
|
r = mbedtls_ecdh_calc_secret(&ctx, &olen, res_APDU+1, MBEDTLS_ECP_MAX_BYTES, random_gen, NULL);
|
|
if (r != 0) {
|
|
mbedtls_ecdh_free(&ctx);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
res_APDU_size = olen+1;
|
|
mbedtls_ecdh_free(&ctx);
|
|
}
|
|
else
|
|
return SW_WRONG_P1P2();
|
|
return SW_OK();
|
|
}
|
|
|
|
static int cmd_cipher_sym() {
|
|
int key_id = P1(apdu);
|
|
int algo = P2(apdu);
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
file_t *ef = search_dynamic_file((KEY_PREFIX << 8) | key_id);
|
|
if (!ef)
|
|
return SW_FILE_NOT_FOUND();
|
|
if ((apdu.cmd_apdu_data_len % 16) != 0) {
|
|
return SW_WRONG_LENGTH();
|
|
}
|
|
int key_size = file_read_uint16(ef->data);
|
|
if (load_dkek() != HSM_OK)
|
|
return SW_EXEC_ERROR();
|
|
uint8_t kdata[32]; //maximum AES key size
|
|
memcpy(kdata, file_read(ef->data+2), key_size);
|
|
if (decrypt(tmp_dkek+IV_SIZE, tmp_dkek, kdata, key_size) != 0) {
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
release_dkek();
|
|
if (algo == ALGO_AES_CBC_ENCRYPT || algo == ALGO_AES_CBC_DECRYPT) {
|
|
mbedtls_aes_context aes;
|
|
mbedtls_aes_init(&aes);
|
|
uint8_t tmp_iv[IV_SIZE];
|
|
memset(tmp_iv, 0, sizeof(tmp_iv));
|
|
if (algo == ALGO_AES_CBC_ENCRYPT) {
|
|
int r = mbedtls_aes_setkey_enc(&aes, kdata, key_size*8);
|
|
if (r != 0) {
|
|
mbedtls_aes_free(&aes);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
r = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, apdu.cmd_apdu_data_len, tmp_iv, apdu.cmd_apdu_data, res_APDU);
|
|
if (r != 0) {
|
|
mbedtls_aes_free(&aes);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
}
|
|
else if (algo == ALGO_AES_CBC_DECRYPT) {
|
|
int r = mbedtls_aes_setkey_dec(&aes, kdata, key_size*8);
|
|
if (r != 0) {
|
|
mbedtls_aes_free(&aes);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
r = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, apdu.cmd_apdu_data_len, tmp_iv, apdu.cmd_apdu_data, res_APDU);
|
|
if (r != 0) {
|
|
mbedtls_aes_free(&aes);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
}
|
|
res_APDU_size = apdu.cmd_apdu_data_len;
|
|
mbedtls_aes_free(&aes);
|
|
}
|
|
else if (algo == ALGO_AES_CMAC) {
|
|
const mbedtls_cipher_info_t *cipher_info;
|
|
if (key_size == 16)
|
|
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB);
|
|
else if (key_size == 24)
|
|
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_192_ECB);
|
|
else if (key_size == 32)
|
|
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_256_ECB);
|
|
else
|
|
return SW_WRONG_DATA();
|
|
int r = mbedtls_cipher_cmac(cipher_info, kdata, key_size*8, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, res_APDU);
|
|
if (r != 0)
|
|
return SW_EXEC_ERROR();
|
|
res_APDU_size = apdu.cmd_apdu_data_len;
|
|
}
|
|
else if (algo == ALGO_AES_DERIVE) {
|
|
int r = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, file_read(ef->data+2), key_size, apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, res_APDU, apdu.cmd_apdu_data_len);
|
|
if (r != 0)
|
|
return SW_EXEC_ERROR();
|
|
res_APDU_size = apdu.cmd_apdu_data_len;
|
|
}
|
|
else {
|
|
return SW_WRONG_P1P2();
|
|
}
|
|
return SW_OK();
|
|
}
|
|
|
|
#define MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED -0x006E
|
|
#define MOD_ADD( N ) \
|
|
while( mbedtls_mpi_cmp_mpi( &(N), &grp->P ) >= 0 ) \
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_sub_abs( &(N), &(N), &grp->P ) )
|
|
static inline int mbedtls_mpi_add_mod( const mbedtls_ecp_group *grp,
|
|
mbedtls_mpi *X,
|
|
const mbedtls_mpi *A,
|
|
const mbedtls_mpi *B )
|
|
{
|
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( X, A, B ) );
|
|
MOD_ADD( *X );
|
|
cleanup:
|
|
return( ret );
|
|
}
|
|
|
|
static int cmd_derive_asym() {
|
|
uint8_t key_id = P1(apdu);
|
|
uint8_t dest_id = P2(apdu);
|
|
file_t *fkey;
|
|
if (!isUserAuthenticated)
|
|
return SW_SECURITY_STATUS_NOT_SATISFIED();
|
|
if (!(fkey = search_dynamic_file((KEY_PREFIX << 8) | key_id)) || !fkey->data)
|
|
return SW_FILE_NOT_FOUND();
|
|
int key_size = file_read_uint16(fkey->data);
|
|
if (apdu.cmd_apdu_data_len == 0)
|
|
return SW_WRONG_LENGTH();
|
|
if (apdu.cmd_apdu_data[0] == ALGO_EC_DERIVE) {
|
|
mbedtls_ecdsa_context ctx;
|
|
mbedtls_ecdsa_init(&ctx);
|
|
|
|
int r;
|
|
r = load_private_key_ecdsa(&ctx, fkey);
|
|
if (r != HSM_OK) {
|
|
mbedtls_ecdsa_free(&ctx);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
mbedtls_mpi a, nd;
|
|
mbedtls_mpi_init(&a);
|
|
mbedtls_mpi_init(&nd);
|
|
r = mbedtls_mpi_read_binary(&a, apdu.cmd_apdu_data+1, apdu.cmd_apdu_data_len-1);
|
|
if (r != 0) {
|
|
mbedtls_ecdsa_free(&ctx);
|
|
mbedtls_mpi_free(&a);
|
|
mbedtls_mpi_free(&nd);
|
|
return SW_DATA_INVALID();
|
|
}
|
|
r = mbedtls_mpi_add_mod(&ctx.grp, &nd, &ctx.d, &a);
|
|
if (r != 0) {
|
|
mbedtls_ecdsa_free(&ctx);
|
|
mbedtls_mpi_free(&a);
|
|
mbedtls_mpi_free(&nd);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
r = mbedtls_mpi_copy(&ctx.d, &nd);
|
|
if (r != 0) {
|
|
mbedtls_ecdsa_free(&ctx);
|
|
mbedtls_mpi_free(&a);
|
|
mbedtls_mpi_free(&nd);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
sc_context_t *card_ctx = create_context();
|
|
r = store_keys(&ctx, SC_PKCS15_TYPE_PRKEY_EC, dest_id, card_ctx);
|
|
free(card_ctx);
|
|
if (r != HSM_OK) {
|
|
mbedtls_ecdsa_free(&ctx);
|
|
mbedtls_mpi_free(&a);
|
|
mbedtls_mpi_free(&nd);
|
|
return SW_EXEC_ERROR();
|
|
}
|
|
mbedtls_ecdsa_free(&ctx);
|
|
mbedtls_mpi_free(&a);
|
|
mbedtls_mpi_free(&nd);
|
|
}
|
|
else
|
|
return SW_WRONG_DATA();
|
|
return SW_OK();
|
|
}
|
|
|
|
typedef struct cmd
|
|
{
|
|
uint8_t ins;
|
|
int (*cmd_handler)();
|
|
} cmd_t;
|
|
|
|
#define INS_VERIFY 0x20
|
|
#define INS_CHANGE_PIN 0x24
|
|
#define INS_RESET_RETRY 0x2C
|
|
#define INS_KEYPAIR_GEN 0x46
|
|
#define INS_KEY_GEN 0x48
|
|
#define INS_INITIALIZE 0x50
|
|
#define INS_IMPORT_DKEK 0x52
|
|
#define INS_LIST_KEYS 0x58
|
|
#define INS_DECRYPT_ASYM 0x62
|
|
#define INS_SIGNATURE 0x68
|
|
#define INS_WRAP 0x72
|
|
#define INS_UNWRAP 0x74
|
|
#define INS_DERIVE_ASYM 0x76
|
|
#define INS_CIPHER_SYM 0x78
|
|
#define INS_CHALLENGE 0x84
|
|
#define INS_SELECT_FILE 0xA4
|
|
#define INS_READ_BINARY 0xB0
|
|
#define INS_READ_BINARY_ODD 0xB1
|
|
#define INS_UPDATE_EF 0xD7
|
|
#define INS_DELETE_FILE 0xE4
|
|
|
|
static const cmd_t cmds[] = {
|
|
{ INS_SELECT_FILE, cmd_select },
|
|
{ INS_LIST_KEYS, cmd_list_keys },
|
|
{ INS_READ_BINARY, cmd_read_binary },
|
|
{ INS_READ_BINARY_ODD, cmd_read_binary },
|
|
{ INS_VERIFY, cmd_verify },
|
|
{ INS_RESET_RETRY, cmd_reset_retry },
|
|
{ INS_CHALLENGE, cmd_challenge },
|
|
{ INS_INITIALIZE, cmd_initialize },
|
|
{ INS_IMPORT_DKEK, cmd_import_dkek },
|
|
{ INS_KEYPAIR_GEN, cmd_keypair_gen },
|
|
{ INS_UPDATE_EF, cmd_update_ef },
|
|
{ INS_DELETE_FILE, cmd_delete_file },
|
|
{ INS_CHANGE_PIN, cmd_change_pin },
|
|
{ INS_KEY_GEN, cmd_key_gen },
|
|
{ INS_SIGNATURE, cmd_signature },
|
|
{ INS_WRAP, cmd_key_wrap },
|
|
{ INS_UNWRAP, cmd_key_unwrap },
|
|
{ INS_DECRYPT_ASYM, cmd_decrypt_asym },
|
|
{ INS_CIPHER_SYM, cmd_cipher_sym },
|
|
{ INS_DERIVE_ASYM, cmd_derive_asym },
|
|
{ 0x00, 0x0}
|
|
};
|
|
|
|
int sc_hsm_process_apdu() {
|
|
for (const cmd_t *cmd = cmds; cmd->ins != 0x00; cmd++) {
|
|
if (cmd->ins == INS(apdu))
|
|
return cmd->cmd_handler();
|
|
}
|
|
return SW_INS_NOT_SUPPORTED();
|
|
} |