diff --git a/CMakeLists.txt b/CMakeLists.txt
index cd98f90..5613867 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,6 +59,32 @@ target_sources(pico_hsm PUBLIC
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/ccid/crypto_utils.c
${CMAKE_CURRENT_LIST_DIR}/pico-ccid/src/ccid/eac.c
${CMAKE_CURRENT_LIST_DIR}/src/hsm/sc_hsm.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_select.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_list_keys.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_read_binary.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_verify.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_reset_retry.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_challenge.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_external_authenticate.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_mse.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_initialize.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_key_domain.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_key_wrap.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_keypair_gen.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_update_ef.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_delete_file.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_change_pin.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_key_gen.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_signature.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_key_unwrap.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_decrypt_asym.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_cipher_sym.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_derive_asym.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_extras.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_general_authenticate.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_session_pin.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_puk_auth.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/hsm/cmd_pso.c
${CMAKE_CURRENT_LIST_DIR}/src/hsm/cvc.c
${CMAKE_CURRENT_LIST_DIR}/src/hsm/files.c
${CMAKE_CURRENT_LIST_DIR}/src/hsm/kek.c
diff --git a/src/hsm/cmd_challenge.c b/src/hsm/cmd_challenge.c
new file mode 100644
index 0000000..6f746b5
--- /dev/null
+++ b/src/hsm/cmd_challenge.c
@@ -0,0 +1,33 @@
+/*
+ * 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 .
+ */
+
+#include "random.h"
+#include "sc_hsm.h"
+
+uint8_t challenge[256];
+uint8_t challenge_len = 0;
+
+int cmd_challenge() {
+ uint8_t *rb = (uint8_t *)random_bytes_get(apdu.ne);
+ if (!rb)
+ return SW_WRONG_LENGTH();
+ memcpy(res_APDU, rb, apdu.ne);
+ challenge_len = MIN(apdu.ne, sizeof(challenge));
+ memcpy(challenge, rb, challenge_len);
+ res_APDU_size = apdu.ne;
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_change_pin.c b/src/hsm/cmd_change_pin.c
new file mode 100644
index 0000000..836fae2
--- /dev/null
+++ b/src/hsm/cmd_change_pin.c
@@ -0,0 +1,55 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "kek.h"
+
+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_get_data(file_pin1));
+ int r = check_pin(file_pin1, apdu.data, pin_len);
+ if (r != 0x9000)
+ return r;
+ uint8_t mkek[MKEK_SIZE];
+ r = load_mkek(mkek); //loads the MKEK with old pin
+ if (r != CCID_OK)
+ return SW_EXEC_ERROR();
+ //encrypt MKEK with new pin
+ hash_multi(apdu.data+pin_len, apdu.nc-pin_len, session_pin);
+ has_session_pin = true;
+ r = store_mkek(mkek);
+ release_mkek(mkek);
+ if (r != CCID_OK)
+ return SW_EXEC_ERROR();
+ uint8_t dhash[33];
+ dhash[0] = apdu.nc-pin_len;
+ double_hash_pin(apdu.data+pin_len, apdu.nc-pin_len, dhash+1);
+ flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
+ low_flash_available();
+ return SW_OK();
+ }
+ }
+ return SW_WRONG_P1P2();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_cipher_sym.c b/src/hsm/cmd_cipher_sym.c
new file mode 100644
index 0000000..4ae4dd3
--- /dev/null
+++ b/src/hsm/cmd_cipher_sym.c
@@ -0,0 +1,113 @@
+/*
+ * 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 .
+ */
+
+#include "common.h"
+#include "mbedtls/aes.h"
+#include "mbedtls/cmac.h"
+#include "mbedtls/hkdf.h"
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "kek.h"
+
+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 (key_has_purpose(ef, algo) == false)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ if ((apdu.nc % 16) != 0) {
+ return SW_WRONG_LENGTH();
+ }
+ if (wait_button() == true) //timeout
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+ int key_size = file_get_size(ef);
+ uint8_t kdata[32]; //maximum AES key size
+ memcpy(kdata, file_get_data(ef), key_size);
+ if (mkek_decrypt(kdata, key_size) != 0) {
+ return SW_EXEC_ERROR();
+ }
+ 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_platform_zeroize(kdata, sizeof(kdata));
+ mbedtls_aes_free(&aes);
+ return SW_EXEC_ERROR();
+ }
+ r = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, apdu.nc, tmp_iv, apdu.data, res_APDU);
+ mbedtls_platform_zeroize(kdata, sizeof(kdata));
+ 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_platform_zeroize(kdata, sizeof(kdata));
+ mbedtls_aes_free(&aes);
+ return SW_EXEC_ERROR();
+ }
+ r = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, apdu.nc, tmp_iv, apdu.data, res_APDU);
+ mbedtls_platform_zeroize(kdata, sizeof(kdata));
+ if (r != 0) {
+ mbedtls_aes_free(&aes);
+ return SW_EXEC_ERROR();
+ }
+ }
+ res_APDU_size = apdu.nc;
+ 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 {
+ mbedtls_platform_zeroize(kdata, sizeof(kdata));
+ return SW_WRONG_DATA();
+ }
+ int r = mbedtls_cipher_cmac(cipher_info, kdata, key_size*8, apdu.data, apdu.nc, res_APDU);
+ mbedtls_platform_zeroize(kdata, sizeof(kdata));
+ if (r != 0)
+ return SW_EXEC_ERROR();
+ res_APDU_size = 16;
+ }
+ else if (algo == ALGO_AES_DERIVE) {
+ int r = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, file_get_data(ef), key_size, apdu.data, apdu.nc, res_APDU, apdu.nc);
+ mbedtls_platform_zeroize(kdata, sizeof(kdata));
+ if (r != 0)
+ return SW_EXEC_ERROR();
+ res_APDU_size = apdu.nc;
+ }
+ else {
+ mbedtls_platform_zeroize(kdata, sizeof(kdata));
+ return SW_WRONG_P1P2();
+ }
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_decrypt_asym.c b/src/hsm/cmd_decrypt_asym.c
new file mode 100644
index 0000000..af6e0dc
--- /dev/null
+++ b/src/hsm/cmd_decrypt_asym.c
@@ -0,0 +1,170 @@
+/*
+ * 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 .
+ */
+
+#include "common.h"
+#include "mbedtls/ecdh.h"
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "kek.h"
+#include "files.h"
+#include "asn1.h"
+#include "cvc.h"
+#include "random.h"
+#include "oid.h"
+
+int cmd_decrypt_asym() {
+ int key_id = P1(apdu);
+ uint8_t p2 = 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 (get_key_counter(ef) == 0)
+ return SW_FILE_FULL();
+ if (key_has_purpose(ef, p2) == false)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ if (p2 >= ALGO_RSA_DECRYPT && p2 <= ALGO_RSA_DECRYPT_OEP) {
+ mbedtls_rsa_context ctx;
+ mbedtls_rsa_init(&ctx);
+ if (p2 == ALGO_RSA_DECRYPT_OEP)
+ mbedtls_rsa_set_padding(&ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_NONE);
+ int r = load_private_key_rsa(&ctx, ef);
+ if (r != CCID_OK) {
+ mbedtls_rsa_free(&ctx);
+ if (r == CCID_VERIFICATION_FAILED)
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+ return SW_EXEC_ERROR();
+ }
+ int key_size = file_get_size(ef);
+ if (apdu.nc < key_size) //needs padding
+ memset(apdu.data+apdu.nc, 0, key_size-apdu.nc);
+ if (p2 == ALGO_RSA_DECRYPT_PKCS1 || p2 == ALGO_RSA_DECRYPT_OEP) {
+ size_t olen = apdu.nc;
+ r = mbedtls_rsa_pkcs1_decrypt(&ctx, random_gen, NULL, &olen, apdu.data, res_APDU, 512);
+ if (r == 0)
+ res_APDU_size = olen;
+ }
+ else {
+ r = mbedtls_rsa_private(&ctx, random_gen, NULL, apdu.data, res_APDU);
+ if (r == 0)
+ res_APDU_size = key_size;
+ }
+ if (r != 0) {
+ mbedtls_rsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ mbedtls_rsa_free(&ctx);
+ }
+ else if (p2 == ALGO_EC_DH || p2 == ALGO_EC_DH_XKEK) {
+ mbedtls_ecdh_context ctx;
+ if (wait_button() == true) //timeout
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+ int key_size = file_get_size(ef);
+ uint8_t *kdata = (uint8_t *)calloc(1,key_size);
+ memcpy(kdata, file_get_data(ef), key_size);
+ if (mkek_decrypt(kdata, key_size) != 0) {
+ mbedtls_platform_zeroize(kdata, key_size);
+ free(kdata);
+ return SW_EXEC_ERROR();
+ }
+ mbedtls_ecdh_init(&ctx);
+ mbedtls_ecp_group_id gid = kdata[0];
+ int r = 0;
+ r = mbedtls_ecdh_setup(&ctx, gid);
+ if (r != 0) {
+ mbedtls_platform_zeroize(kdata, key_size);
+ 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);
+ mbedtls_platform_zeroize(kdata, key_size);
+ free(kdata);
+ if (r != 0) {
+ mbedtls_ecdh_free(&ctx);
+ return SW_DATA_INVALID();
+ }
+ r = -1;
+ if (p2 == ALGO_EC_DH)
+ r = mbedtls_ecdh_read_public(&ctx, apdu.data-1, apdu.nc+1);
+ else if (p2 == ALGO_EC_DH_XKEK) {
+ size_t pub_len = 0;
+ const uint8_t *pub = cvc_get_pub(apdu.data, apdu.nc, &pub_len);
+ if (pub) {
+ size_t t86_len = 0;
+ const uint8_t *t86 = cvc_get_field(pub, pub_len, &t86_len, 0x86);
+ if (t86) {
+ r = mbedtls_ecdh_read_public(&ctx, t86-1, t86_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();
+ }
+ if (p2 == ALGO_EC_DH)
+ res_APDU_size = olen+1;
+ else {
+ res_APDU_size = 0;
+ size_t ext_len = 0;
+ const uint8_t *ext = NULL;
+ if ((ext = cvc_get_ext(apdu.data, apdu.nc, &ext_len)) == NULL)
+ return SW_WRONG_DATA();
+ uint8_t *p = NULL, *tag_data = NULL, *kdom_uid = NULL;
+ uint16_t tag = 0;
+ size_t tag_len = 0, kdom_uid_len = 0;
+ while (walk_tlv(ext, ext_len, &p, &tag, &tag_len, &tag_data)) {
+ if (tag == 0x73) {
+ size_t oid_len = 0;
+ uint8_t *oid_data = NULL;
+ if (asn1_find_tag(tag_data, tag_len, 0x6, &oid_len, &oid_data) == true && oid_len == strlen(OID_ID_KEY_DOMAIN_UID) && memcmp(oid_data, OID_ID_KEY_DOMAIN_UID, strlen(OID_ID_KEY_DOMAIN_UID)) == 0) {
+ if (asn1_find_tag(tag_data, tag_len, 0x80, &kdom_uid_len, &kdom_uid) == false)
+ return SW_WRONG_DATA();
+ break;
+ }
+ }
+ }
+ if (kdom_uid_len == 0 || kdom_uid == NULL)
+ return SW_WRONG_DATA();
+ for (int n = 0; n < MAX_KEY_DOMAINS; n++) {
+ file_t *tf = search_dynamic_file(EF_XKEK+n);
+ if (tf) {
+ if (file_get_size(tf) == kdom_uid_len && memcmp(file_get_data(tf), kdom_uid, kdom_uid_len) == 0) {
+ file_new(EF_DKEK+n);
+ if (store_dkek_key(n, res_APDU+1) != CCID_OK)
+ return SW_EXEC_ERROR();
+ return SW_OK();
+ }
+ }
+ }
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ mbedtls_ecdh_free(&ctx);
+ }
+ else
+ return SW_WRONG_P1P2();
+ decrement_key_counter(ef);
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_delete_file.c b/src/hsm/cmd_delete_file.c
new file mode 100644
index 0000000..787f146
--- /dev/null
+++ b/src/hsm/cmd_delete_file.c
@@ -0,0 +1,40 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+
+int cmd_delete_file() {
+ file_t *ef = NULL;
+ if (!isUserAuthenticated)
+ return SW_SECURITY_STATUS_NOT_SATISFIED();
+
+ if (apdu.nc == 0) {
+ ef = currentEF;
+ if (!(ef = search_dynamic_file(ef->fid)))
+ return SW_FILE_NOT_FOUND();
+ }
+ else {
+ uint16_t fid = (apdu.data[0] << 8) | 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 (delete_file(ef) != CCID_OK)
+ return SW_EXEC_ERROR();
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_derive_asym.c b/src/hsm/cmd_derive_asym.c
new file mode 100644
index 0000000..907a2ec
--- /dev/null
+++ b/src/hsm/cmd_derive_asym.c
@@ -0,0 +1,102 @@
+/*
+ * 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 .
+ */
+
+#include "common.h"
+#include "mbedtls/ecdsa.h"
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+
+
+#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 );
+}
+
+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 || file_get_size(fkey) == 0)
+ return SW_FILE_NOT_FOUND();
+ if (key_has_purpose(fkey, ALGO_EC_DERIVE) == false)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ if (apdu.nc == 0)
+ return SW_WRONG_LENGTH();
+ if (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 != CCID_OK) {
+ mbedtls_ecdsa_free(&ctx);
+ if (r == CCID_VERIFICATION_FAILED)
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+ return SW_EXEC_ERROR();
+ }
+ mbedtls_mpi a, nd;
+ mbedtls_mpi_init(&a);
+ mbedtls_mpi_init(&nd);
+ r = mbedtls_mpi_read_binary(&a, apdu.data+1, apdu.nc-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();
+ }
+ r = store_keys(&ctx, HSM_KEY_EC, dest_id);
+ if (r != CCID_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();
+}
diff --git a/src/hsm/cmd_external_authenticate.c b/src/hsm/cmd_external_authenticate.c
new file mode 100644
index 0000000..0387f31
--- /dev/null
+++ b/src/hsm/cmd_external_authenticate.c
@@ -0,0 +1,54 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "cvc.h"
+#include "files.h"
+
+extern file_t *ef_puk_aut;
+extern uint8_t challenge[256];
+extern uint8_t challenge_len;
+
+int cmd_external_authenticate() {
+ if (P1(apdu) != 0x0 || P2(apdu) != 0x0)
+ return SW_INCORRECT_P1P2();
+ if (ef_puk_aut == NULL)
+ return SW_REFERENCE_NOT_FOUND();
+ if (apdu.nc == 0)
+ return SW_WRONG_LENGTH();
+ file_t *ef_puk = search_by_fid(EF_PUKAUT, NULL, SPECIFY_EF);
+ if (!ef_puk || !ef_puk->data || file_get_size(ef_puk) == 0)
+ return SW_FILE_NOT_FOUND();
+ uint8_t *puk_data = file_get_data(ef_puk);
+ uint8_t *input = (uint8_t *)calloc(dev_name_len+challenge_len, sizeof(uint8_t)), hash[32];
+ memcpy(input, dev_name, dev_name_len);
+ memcpy(input+dev_name_len, challenge, challenge_len);
+ hash256(input, dev_name_len+challenge_len, hash);
+ int r = puk_verify(apdu.data, apdu.nc, hash, 32, file_get_data(ef_puk_aut), file_get_size(ef_puk_aut));
+ free(input);
+ if (r != 0)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ puk_status[ef_puk_aut->fid & (MAX_PUK-1)] = 1;
+ uint8_t auts = 0;
+ for (int i = 0; i < puk_data[0]; i++)
+ auts += puk_status[i];
+ if (auts >= puk_data[2]) {
+ isUserAuthenticated = true;
+ }
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_extras.c b/src/hsm/cmd_extras.c
new file mode 100644
index 0000000..e846635
--- /dev/null
+++ b/src/hsm/cmd_extras.c
@@ -0,0 +1,72 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+#include "hardware/rtc.h"
+#include "files.h"
+
+int cmd_extras() {
+ if (P2(apdu) != 0x0)
+ return SW_INCORRECT_P1P2();
+ if (P1(apdu) == 0xA) { //datetime operations
+ if (apdu.nc == 0) {
+ datetime_t dt;
+ if (!rtc_get_datetime(&dt))
+ return SW_EXEC_ERROR();
+ res_APDU[res_APDU_size++] = dt.year >> 8;
+ res_APDU[res_APDU_size++] = dt.year & 0xff;
+ res_APDU[res_APDU_size++] = dt.month;
+ res_APDU[res_APDU_size++] = dt.day;
+ res_APDU[res_APDU_size++] = dt.dotw;
+ res_APDU[res_APDU_size++] = dt.hour;
+ res_APDU[res_APDU_size++] = dt.min;
+ res_APDU[res_APDU_size++] = dt.sec;
+ }
+ else {
+ if (apdu.nc != 8)
+ return SW_WRONG_LENGTH();
+ datetime_t dt;
+ dt.year = (apdu.data[0] << 8) | (apdu.data[1]);
+ dt.month = apdu.data[2];
+ dt.day = apdu.data[3];
+ dt.dotw = apdu.data[4];
+ dt.hour = apdu.data[5];
+ dt.min = apdu.data[6];
+ dt.sec = apdu.data[7];
+ if (!rtc_set_datetime(&dt))
+ return SW_WRONG_DATA();
+ }
+ }
+ else if (P1(apdu) == 0x6) { //dynamic options
+ if (apdu.nc > sizeof(uint8_t))
+ return SW_WRONG_LENGTH();
+ uint16_t opts = get_device_options();
+ if (apdu.nc == 0) {
+ res_APDU[res_APDU_size++] = opts >> 8;
+ res_APDU[res_APDU_size++] = opts & 0xff;
+ }
+ else {
+ uint8_t newopts[] = { apdu.data[0], (opts & 0xff) };
+ file_t *tf = search_by_fid(EF_DEVOPS, NULL, SPECIFY_EF);
+ flash_write_data_to_file(tf, newopts, sizeof(newopts));
+ low_flash_available();
+ }
+ }
+ else
+ return SW_INCORRECT_P1P2();
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_general_authenticate.c b/src/hsm/cmd_general_authenticate.c
new file mode 100644
index 0000000..536d09b
--- /dev/null
+++ b/src/hsm/cmd_general_authenticate.c
@@ -0,0 +1,98 @@
+/*
+ * 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 .
+ */
+
+#include "common.h"
+#include "mbedtls/ecdh.h"
+#include "asn1.h"
+#include "sc_hsm.h"
+#include "random.h"
+#include "cvcerts.h"
+#include "oid.h"
+#include "eac.h"
+
+int cmd_general_authenticate() {
+ if (P1(apdu) == 0x0 && P2(apdu) == 0x0) {
+ if (apdu.data[0] == 0x7C) {
+ int r = 0;
+ size_t pubkey_len = 0;
+ const uint8_t *pubkey = NULL;
+ uint16_t tag = 0x0;
+ uint8_t *tag_data = NULL, *p = NULL;
+ size_t tag_len = 0;
+ while (walk_tlv(apdu.data+2, apdu.nc-2, &p, &tag, &tag_len, &tag_data)) {
+ if (tag == 0x80) {
+ pubkey = tag_data-1; //mbedtls ecdh starts reading one pos before
+ pubkey_len = tag_len+1;
+ }
+ }
+ mbedtls_ecdh_context ctx;
+ int key_size = file_read_uint16(termca_pk);
+ mbedtls_ecdh_init(&ctx);
+ mbedtls_ecp_group_id gid = MBEDTLS_ECP_DP_SECP192R1;
+ r = mbedtls_ecdh_setup(&ctx, gid);
+ if (r != 0) {
+ mbedtls_ecdh_free(&ctx);
+ return SW_DATA_INVALID();
+ }
+ r = mbedtls_mpi_read_binary(&ctx.ctx.mbed_ecdh.d, termca_pk+2, key_size);
+ if (r != 0) {
+ mbedtls_ecdh_free(&ctx);
+ return SW_DATA_INVALID();
+ }
+ r = mbedtls_ecdh_read_public(&ctx, pubkey, pubkey_len);
+ if (r != 0) {
+ mbedtls_ecdh_free(&ctx);
+ return SW_DATA_INVALID();
+ }
+ size_t olen = 0;
+ uint8_t derived[MBEDTLS_ECP_MAX_BYTES];
+ r = mbedtls_ecdh_calc_secret(&ctx, &olen, derived, MBEDTLS_ECP_MAX_BYTES, random_gen, NULL);
+ mbedtls_ecdh_free(&ctx);
+ if (r != 0) {
+ return SW_EXEC_ERROR();
+ }
+
+ sm_derive_all_keys(derived, olen);
+
+ uint8_t *t = (uint8_t *)calloc(1, pubkey_len+16);
+ memcpy(t, "\x7F\x49\x3F\x06\x0A", 5);
+ if (sm_get_protocol() == MSE_AES)
+ memcpy(t+5, OID_ID_CA_ECDH_AES_CBC_CMAC_128, 10);
+ else if (sm_get_protocol() == MSE_3DES)
+ memcpy(t+5, OID_ID_CA_ECDH_3DES_CBC_CBC, 10);
+ t[15] = 0x86;
+ memcpy(t+16, pubkey, pubkey_len);
+
+ res_APDU[res_APDU_size++] = 0x7C;
+ res_APDU[res_APDU_size++] = 20;
+ res_APDU[res_APDU_size++] = 0x81;
+ res_APDU[res_APDU_size++] = 8;
+ memcpy(res_APDU+res_APDU_size, sm_get_nonce(), 8);
+ res_APDU_size += 8;
+ res_APDU[res_APDU_size++] = 0x82;
+ res_APDU[res_APDU_size++] = 8;
+
+ r = sm_sign(t, pubkey_len+16, res_APDU+res_APDU_size);
+
+ free(t);
+ if (r != CCID_OK)
+ return SW_EXEC_ERROR();
+ res_APDU_size += 8;
+ }
+ }
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_initialize.c b/src/hsm/cmd_initialize.c
new file mode 100644
index 0000000..3a819d1
--- /dev/null
+++ b/src/hsm/cmd_initialize.c
@@ -0,0 +1,160 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+#include "files.h"
+#include "random.h"
+#include "kek.h"
+#include "version.h"
+#include "asn1.h"
+
+extern void scan_all();
+
+extern char __StackLimit;
+int heapLeft() {
+ char *p = malloc(256); // try to avoid undue fragmentation
+ int left = &__StackLimit - p;
+ free(p);
+ return left;
+}
+
+int cmd_initialize() {
+ if (apdu.nc > 0) {
+ initialize_flash(true);
+ scan_all();
+ has_session_pin = has_session_sopin = false;
+ uint16_t tag = 0x0;
+ uint8_t *tag_data = NULL, *p = NULL, *kds = NULL, *dkeks = NULL;
+ size_t tag_len = 0;
+ while (walk_tlv(apdu.data, apdu.nc, &p, &tag, &tag_len, &tag_data)) {
+ if (tag == 0x80) { //options
+ file_t *tf = search_by_fid(EF_DEVOPS, NULL, SPECIFY_EF);
+ flash_write_data_to_file(tf, tag_data, tag_len);
+ }
+ else if (tag == 0x81) { //user pin
+ if (file_pin1 && file_pin1->data) {
+ uint8_t dhash[33];
+ dhash[0] = tag_len;
+ double_hash_pin(tag_data, tag_len, dhash+1);
+ flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
+ hash_multi(tag_data, tag_len, session_pin);
+ has_session_pin = true;
+ }
+ }
+ else if (tag == 0x82) { //sopin pin
+ if (file_sopin && file_sopin->data) {
+ uint8_t dhash[33];
+ dhash[0] = tag_len;
+ double_hash_pin(tag_data, tag_len, dhash+1);
+ flash_write_data_to_file(file_sopin, dhash, sizeof(dhash));
+ hash_multi(tag_data, tag_len, session_sopin);
+ has_session_sopin = true;
+ }
+ }
+ else if (tag == 0x91) { //retries user pin
+ file_t *tf = search_by_fid(0x1082, NULL, SPECIFY_EF);
+ if (tf && tf->data) {
+ flash_write_data_to_file(tf, tag_data, tag_len);
+ }
+ if (file_retries_pin1 && file_retries_pin1->data) {
+ flash_write_data_to_file(file_retries_pin1, tag_data, tag_len);
+ }
+ }
+ else if (tag == 0x92) {
+ dkeks = tag_data;
+ file_t *tf = file_new(EF_DKEK);
+ if (!tf)
+ return SW_MEMORY_FAILURE();
+ flash_write_data_to_file(tf, NULL, 0);
+ }
+ else if (tag == 0x93) {
+ file_t *ef_puk = search_by_fid(EF_PUKAUT, NULL, SPECIFY_EF);
+ if (!ef_puk)
+ return SW_MEMORY_FAILURE();
+ uint8_t pk_status[4], puks = MIN(tag_data[0],MAX_PUK);
+ memset(pk_status, 0, sizeof(pk_status));
+ pk_status[0] = puks;
+ pk_status[1] = puks;
+ pk_status[2] = tag_data[1];
+ flash_write_data_to_file(ef_puk, pk_status, sizeof(pk_status));
+ for (int i = 0; i < puks; i++) {
+ file_t *tf = file_new(EF_PUK+i);
+ if (!tf)
+ return SW_MEMORY_FAILURE();
+ flash_write_data_to_file(tf, NULL, 0);
+ }
+ }
+ else if (tag == 0x97) {
+ kds = tag_data;
+ /*
+ for (int i = 0; i < MIN(*kds,MAX_KEY_DOMAINS); i++) {
+ file_t *tf = file_new(EF_DKEK+i);
+ if (!tf)
+ return SW_MEMORY_FAILURE();
+ flash_write_data_to_file(tf, NULL, 0);
+ }
+ */
+ }
+ }
+ file_t *tf_kd = search_by_fid(EF_KEY_DOMAIN, NULL, SPECIFY_EF);
+ if (!tf_kd)
+ return SW_EXEC_ERROR();
+ if (store_mkek(NULL) != CCID_OK)
+ return SW_EXEC_ERROR();
+ if (dkeks) {
+ if (*dkeks > 0) {
+ uint16_t d = *dkeks;
+ if (flash_write_data_to_file(tf_kd, (const uint8_t *)&d, sizeof(d)) != CCID_OK)
+ return SW_EXEC_ERROR();
+ }
+ else {
+ int r = save_dkek_key(0, random_bytes_get(32));
+ if (r != CCID_OK)
+ return SW_EXEC_ERROR();
+ uint16_t d = 0x0101;
+ if (flash_write_data_to_file(tf_kd, (const uint8_t *)&d, sizeof(d)) != CCID_OK)
+ return SW_EXEC_ERROR();
+ }
+ }
+ else {
+ uint16_t d = 0x0000;
+ if (flash_write_data_to_file(tf_kd, (const uint8_t *)&d, sizeof(d)) != CCID_OK)
+ return SW_EXEC_ERROR();
+ }
+ if (kds) {
+ uint8_t t[MAX_KEY_DOMAINS*2], k = MIN(*kds,MAX_KEY_DOMAINS);
+ memset(t, 0xff, 2*k);
+ if (flash_write_data_to_file(tf_kd, t, 2*k) != CCID_OK)
+ return SW_EXEC_ERROR();
+ }
+ /* When initialized, it has all credentials */
+ isUserAuthenticated = true;
+ low_flash_available();
+ }
+ else { //free memory bytes request
+ int heap_left = heapLeft();
+ res_APDU[0] = ((heap_left >> 24) & 0xff);
+ res_APDU[1] = ((heap_left >> 16) & 0xff);
+ res_APDU[2] = ((heap_left >> 8) & 0xff);
+ res_APDU[3] = ((heap_left >> 0) & 0xff);
+ res_APDU[4] = 0;
+ res_APDU[5] = HSM_VERSION_MAJOR;
+ res_APDU[6] = HSM_VERSION_MINOR;
+ res_APDU_size = 7;
+ }
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_key_domain.c b/src/hsm/cmd_key_domain.c
new file mode 100644
index 0000000..d3285b3
--- /dev/null
+++ b/src/hsm/cmd_key_domain.c
@@ -0,0 +1,161 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "cvc.h"
+#include "kek.h"
+#include "files.h"
+#include "cvcerts.h"
+
+uint8_t get_key_domain(file_t *fkey) {
+ size_t tag_len = 0;
+ const uint8_t *meta_tag = get_meta_tag(fkey, 0x92, &tag_len);
+ if (meta_tag)
+ return *meta_tag;
+ return 0xff;
+}
+
+int cmd_key_domain() {
+ //if (dkeks == 0)
+ // return SW_COMMAND_NOT_ALLOWED();
+ uint8_t p1 = P1(apdu), p2 = P2(apdu);
+ if ((has_session_pin == false || isUserAuthenticated == false) && apdu.nc > 0)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ if (p2 >= MAX_KEY_DOMAINS)
+ return SW_WRONG_P1P2();
+ file_t *tf_kd = search_by_fid(EF_KEY_DOMAIN, NULL, SPECIFY_EF);
+ if (!tf_kd)
+ return SW_EXEC_ERROR();
+ uint16_t tf_kd_size = file_get_size(tf_kd);
+ if (tf_kd_size == 0)
+ return SW_WRONG_P1P2();
+ uint8_t *kdata = file_get_data(tf_kd), dkeks = kdata ? kdata[2*p2] : 0, current_dkeks = kdata ? kdata[2*p2+1] : 0;
+ if (p1 == 0x0) { //dkek import
+ if (apdu.nc > 0) {
+ file_t *tf = file_new(EF_DKEK+p2);
+ if (!tf)
+ return SW_MEMORY_FAILURE();
+ if (apdu.nc < 32)
+ return SW_WRONG_LENGTH();
+ import_dkek_share(p2, apdu.data);
+ if (++current_dkeks >= dkeks) {
+ if (save_dkek_key(p2, NULL) != CCID_OK)
+ return SW_FILE_NOT_FOUND();
+ }
+ uint8_t t[MAX_KEY_DOMAINS*2];
+ memcpy(t, kdata, tf_kd_size);
+ t[2*p2+1] = current_dkeks;
+ if (flash_write_data_to_file(tf_kd, t, tf_kd_size) != CCID_OK)
+ return SW_EXEC_ERROR();
+ low_flash_available();
+ }
+ else {
+ file_t *tf = search_dynamic_file(EF_XKEK+p2);
+ if (2*p2 >= tf_kd_size)
+ return SW_INCORRECT_P1P2();
+ if (current_dkeks == 0xff && !tf) //XKEK have always 0xff
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ }
+ else if (p1 == 0x1 || p1 == 0x3 || p1 == 0x4) { //key domain setup
+ if (p1 == 0x1 && apdu.nc != 1)
+ return SW_WRONG_LENGTH();
+ if (p1 == 0x3) { //if key domain is not empty, command is denied
+ for (int i = 0; i < dynamic_files; i++) {
+ if (get_key_domain(&dynamic_file[i]) == p2)
+ return SW_FILE_EXISTS();
+ }
+ }
+ uint8_t t[MAX_KEY_DOMAINS*2];
+ memcpy(t, kdata, tf_kd_size);
+ if (p1 == 0x1) {
+ t[2*p2] = dkeks = apdu.data[0];
+ t[2*p2+1] = current_dkeks = 0;
+ }
+ else if (p1 == 0x3) {
+ t[2*p2] = dkeks = 0xff;
+ t[2*p2+1] = 0xff;
+ }
+ else if (p1 == 0x4) {
+ t[2*p2+1] = current_dkeks = 0;
+ }
+ if (flash_write_data_to_file(tf_kd, t, tf_kd_size) != CCID_OK)
+ return SW_EXEC_ERROR();
+ file_t *tf = NULL;
+ if ((tf = search_dynamic_file(EF_DKEK+p2))) {
+ if (delete_file(tf) != CCID_OK)
+ return SW_EXEC_ERROR();
+ }
+ if (p1 == 0x3 && (tf = search_dynamic_file(EF_XKEK+p2))) {
+ if (delete_file(tf) != CCID_OK)
+ return SW_EXEC_ERROR();
+ }
+ low_flash_available();
+ }
+ else if (p1 == 0x2) { //XKEK Key Domain creation
+ if (apdu.nc > 0) {
+ size_t pub_len = 0;
+ const uint8_t *pub = cvc_get_pub(termca+2, (termca[1] << 8 | termca[0]), &pub_len);
+ if (!pub)
+ return SW_EXEC_ERROR();
+ size_t t86_len = 0;
+ const uint8_t *t86 = cvc_get_field(pub, pub_len, &t86_len, 0x86);
+ if (!t86 || t86[0] != 0x4)
+ return SW_EXEC_ERROR();
+ size_t t54_len = 0;
+ const uint8_t *t54 = cvc_get_field(apdu.data, apdu.nc, &t54_len, 0x54);
+ if (!t54)
+ return SW_WRONG_DATA();
+ uint8_t hash[32], *input = (uint8_t *)calloc(1, (t86_len-1)/2+1);
+ input[0] = 0x54;
+ memcpy(input+1, t86+1, (t86_len-1)/2);
+ hash256(input, (t86_len-1)/2+1, hash);
+ free(input);
+ int r = puk_verify(t54, t54_len, hash, 32, apdu.data, apdu.nc);
+ if (r != 0)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ file_t *tf = file_new(EF_XKEK+p2);
+ if (!tf)
+ return SW_MEMORY_FAILURE();
+
+ //All checks done. Get Key Domain UID
+ pub = cvc_get_pub(apdu.data, apdu.nc, &pub_len);
+ if (pub) {
+ size_t t86_len = 0;
+ const uint8_t *t86 = cvc_get_field(pub, pub_len, &t86_len, 0x86);
+ if (t86) {
+ flash_write_data_to_file(tf, t86+1, t86_len-1);
+ low_flash_available();
+ }
+ }
+ }
+ }
+ else
+ return SW_INCORRECT_P1P2();
+ memset(res_APDU,0,10);
+ res_APDU[0] = dkeks;
+ res_APDU[1] = dkeks > current_dkeks ? dkeks-current_dkeks : 0;
+ dkek_kcv(p2, res_APDU+2);
+ res_APDU_size = 2+8;
+ file_t *tf = search_dynamic_file(EF_XKEK+p2);
+ if (tf) {
+ memcpy(res_APDU+10, file_get_data(tf), file_get_size(tf));
+ res_APDU_size += file_get_size(tf);
+ }
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_key_gen.c b/src/hsm/cmd_key_gen.c
new file mode 100644
index 0000000..00f2fd6
--- /dev/null
+++ b/src/hsm/cmd_key_gen.c
@@ -0,0 +1,52 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "random.h"
+
+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);
+ int aes_type = 0x0;
+ if (key_size == 16)
+ aes_type = HSM_KEY_AES_128;
+ else if (key_size == 24)
+ aes_type = HSM_KEY_AES_192;
+ else if (key_size == 32)
+ aes_type = HSM_KEY_AES_256;
+ r = store_keys(aes_key, aes_type, key_id);
+ if (r != CCID_OK)
+ return SW_MEMORY_FAILURE();
+ if (find_and_store_meta_key(key_id) != CCID_OK)
+ return SW_EXEC_ERROR();
+ low_flash_available();
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_key_unwrap.c b/src/hsm/cmd_key_unwrap.c
new file mode 100644
index 0000000..fa2070d
--- /dev/null
+++ b/src/hsm/cmd_key_unwrap.c
@@ -0,0 +1,106 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "kek.h"
+
+int cmd_key_unwrap() {
+ int key_id = P1(apdu), r = 0;
+ if (P2(apdu) != 0x93)
+ return SW_WRONG_P1P2();
+ if (!isUserAuthenticated)
+ return SW_SECURITY_STATUS_NOT_SATISFIED();
+ int key_type = dkek_type_key(apdu.data);
+ uint8_t kdom = -1, *allowed = NULL;
+ size_t allowed_len = 0;
+ if (key_type == 0x0)
+ return SW_DATA_INVALID();
+ if (key_type == HSM_KEY_RSA) {
+ mbedtls_rsa_context ctx;
+ mbedtls_rsa_init(&ctx);
+ do {
+ r = dkek_decode_key(++kdom, &ctx, apdu.data, apdu.nc, NULL, &allowed, &allowed_len);
+ } while((r == CCID_ERR_FILE_NOT_FOUND || r == CCID_WRONG_DKEK) && kdom < MAX_KEY_DOMAINS);
+ if (r != CCID_OK) {
+ mbedtls_rsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = store_keys(&ctx, HSM_KEY_RSA, key_id);
+ mbedtls_rsa_free(&ctx);
+ if (r != CCID_OK) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ else if (key_type == HSM_KEY_EC) {
+ mbedtls_ecdsa_context ctx;
+ mbedtls_ecdsa_init(&ctx);
+ do {
+ r = dkek_decode_key(++kdom, &ctx, apdu.data, apdu.nc, NULL, &allowed, &allowed_len);
+ } while((r == CCID_ERR_FILE_NOT_FOUND || r == CCID_WRONG_DKEK) && kdom < MAX_KEY_DOMAINS);
+ if (r != CCID_OK) {
+ mbedtls_ecdsa_free(&ctx);
+ return SW_EXEC_ERROR();
+ }
+ r = store_keys(&ctx, HSM_KEY_EC, key_id);
+ mbedtls_ecdsa_free(&ctx);
+ if (r != CCID_OK) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ else if (key_type == HSM_KEY_AES) {
+ uint8_t aes_key[32];
+ int key_size = 0, aes_type = 0;
+ do {
+ r = dkek_decode_key(++kdom, aes_key, apdu.data, apdu.nc, &key_size, &allowed, &allowed_len);
+ } while((r == CCID_ERR_FILE_NOT_FOUND || r == CCID_WRONG_DKEK) && kdom < MAX_KEY_DOMAINS);
+ if (r != CCID_OK) {
+ return SW_EXEC_ERROR();
+ }
+ if (key_size == 32)
+ aes_type = HSM_KEY_AES_256;
+ else if (key_size == 24)
+ aes_type = HSM_KEY_AES_192;
+ else if (key_size == 16)
+ aes_type = HSM_KEY_AES_128;
+ else
+ return SW_EXEC_ERROR();
+ r = store_keys(aes_key, aes_type, key_id);
+ if (r != CCID_OK) {
+ return SW_EXEC_ERROR();
+ }
+ }
+ if ((allowed != NULL && allowed_len > 0) || kdom >= 0) {
+ size_t meta_len = (allowed_len > 0 ? 2+allowed_len : 0) + (kdom >= 0 ? 3 : 0);
+ uint8_t *meta = (uint8_t *)calloc(1,meta_len), *m = meta;
+ if (allowed_len > 0) {
+ *m++ = 0x91;
+ *m++ = allowed_len;
+ memcpy(m, allowed, allowed_len); m += allowed_len;
+ }
+ if (kdom >= 0) {
+ *m++ = 0x92;
+ *m++ = 1;
+ *m++ = kdom;
+ }
+ r = meta_add((KEY_PREFIX << 8) | key_id, meta, meta_len);
+ free(meta);
+ if (r != CCID_OK)
+ return r;
+ }
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_key_wrap.c b/src/hsm/cmd_key_wrap.c
new file mode 100644
index 0000000..78de003
--- /dev/null
+++ b/src/hsm/cmd_key_wrap.c
@@ -0,0 +1,93 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "asn1.h"
+#include "kek.h"
+
+extern uint8_t get_key_domain(file_t *fkey);
+
+int cmd_key_wrap() {
+ int key_id = P1(apdu), r = 0;
+ 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);
+ uint8_t kdom = get_key_domain(ef);
+ if (!ef)
+ return SW_FILE_NOT_FOUND();
+ if (key_has_purpose(ef, ALGO_WRAP) == false)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ file_t *prkd = search_dynamic_file((PRKD_PREFIX << 8) | key_id);
+ if (!prkd)
+ return SW_FILE_NOT_FOUND();
+ const uint8_t *dprkd = file_get_data(prkd);
+ size_t wrap_len = MAX_DKEK_ENCODE_KEY_BUFFER;
+ size_t tag_len = 0;
+ const uint8_t *meta_tag = get_meta_tag(ef, 0x91, &tag_len);
+ if (*dprkd == P15_KEYTYPE_RSA) {
+ mbedtls_rsa_context ctx;
+ mbedtls_rsa_init(&ctx);
+ r = load_private_key_rsa(&ctx, ef);
+ if (r != CCID_OK) {
+ mbedtls_rsa_free(&ctx);
+ if (r == CCID_VERIFICATION_FAILED)
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+ return SW_EXEC_ERROR();
+ }
+ r = dkek_encode_key(kdom, &ctx, HSM_KEY_RSA, res_APDU, &wrap_len, meta_tag, tag_len);
+ mbedtls_rsa_free(&ctx);
+ }
+ else if (*dprkd == P15_KEYTYPE_ECC) {
+ mbedtls_ecdsa_context ctx;
+ mbedtls_ecdsa_init(&ctx);
+ r = load_private_key_ecdsa(&ctx, ef);
+ if (r != CCID_OK) {
+ mbedtls_ecdsa_free(&ctx);
+ if (r == CCID_VERIFICATION_FAILED)
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+ return SW_EXEC_ERROR();
+ }
+ r = dkek_encode_key(kdom, &ctx, HSM_KEY_EC, res_APDU, &wrap_len, meta_tag, tag_len);
+ mbedtls_ecdsa_free(&ctx);
+ }
+ else if (*dprkd == P15_KEYTYPE_AES) {
+ uint8_t kdata[32]; //maximum AES key size
+ if (wait_button() == true) //timeout
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+
+ int key_size = file_get_size(ef), aes_type = HSM_KEY_AES;
+ memcpy(kdata, file_get_data(ef), key_size);
+ if (mkek_decrypt(kdata, key_size) != 0) {
+ return SW_EXEC_ERROR();
+ }
+ if (key_size == 32)
+ aes_type = HSM_KEY_AES_256;
+ else if (key_size == 24)
+ aes_type = HSM_KEY_AES_192;
+ else if (key_size == 16)
+ aes_type = HSM_KEY_AES_128;
+ r = dkek_encode_key(kdom, kdata, aes_type, res_APDU, &wrap_len, meta_tag, tag_len);
+ mbedtls_platform_zeroize(kdata, sizeof(kdata));
+ }
+ if (r != CCID_OK)
+ return SW_EXEC_ERROR();
+ res_APDU_size = wrap_len;
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_keypair_gen.c b/src/hsm/cmd_keypair_gen.c
new file mode 100644
index 0000000..8a7bbbc
--- /dev/null
+++ b/src/hsm/cmd_keypair_gen.c
@@ -0,0 +1,149 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "files.h"
+#include "asn1.h"
+#include "cvc.h"
+#include "oid.h"
+#include "random.h"
+#include "kek.h"
+
+int cmd_keypair_gen() {
+ uint8_t key_id = P1(apdu);
+ if (!isUserAuthenticated)
+ return SW_SECURITY_STATUS_NOT_SATISFIED();
+ int ret = 0;
+
+ size_t tout = 0;
+ //sc_asn1_print_tags(apdu.data, apdu.nc);
+ uint8_t *p = NULL;
+ if (asn1_find_tag(apdu.data, apdu.nc, 0x7f49, &tout, &p) && tout > 0 && p != NULL) {
+ size_t oid_len = 0;
+ uint8_t *oid = NULL;
+ if (asn1_find_tag(p, tout, 0x6, &oid_len, &oid) && oid_len > 0 && oid != NULL) {
+ if (memcmp(oid, OID_ID_TA_RSA_V1_5_SHA_256, oid_len) == 0) { //RSA
+ size_t ex_len = 3, ks_len = 2;
+ uint8_t *ex = NULL, *ks = NULL;
+ uint32_t exponent = 65537, key_size = 2048;
+ if (asn1_find_tag(p, tout, 0x82, &ex_len, &ex) && ex_len > 0 && ex != NULL) {
+ uint8_t *dt = ex;
+ exponent = 0;
+ for (int i = 0; i < ex_len; i++) {
+ exponent = (exponent << 8) | *dt++;
+ }
+ }
+ if (asn1_find_tag(p, tout, 0x2, &ks_len, &ks) && ks_len > 0 && ks != NULL) {
+ uint8_t *dt = ks;
+ key_size = 0;
+ for (int i = 0; i < ks_len; i++) {
+ key_size = (key_size << 8) | *dt++;
+ }
+ }
+ printf("KEYPAIR RSA %ld (%lx)\r\n",key_size,exponent);
+ 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) {
+ mbedtls_rsa_free(&rsa);
+ return SW_EXEC_ERROR();
+ }
+ if ((res_APDU_size = asn1_cvc_aut(&rsa, HSM_KEY_RSA, res_APDU, 4096, NULL, 0)) == 0) {
+ return SW_EXEC_ERROR();
+ }
+ ret = store_keys(&rsa, HSM_KEY_RSA, key_id);
+ if (ret != CCID_OK) {
+ mbedtls_rsa_free(&rsa);
+ return SW_EXEC_ERROR();
+ }
+ mbedtls_rsa_free(&rsa);
+ }
+ else if (memcmp(oid, OID_IT_TA_ECDSA_SHA_256,MIN(oid_len,10)) == 0) { //ECC
+ size_t prime_len;
+ uint8_t *prime = NULL;
+ if (asn1_find_tag(p, tout, 0x81, &prime_len, &prime) != true)
+ return SW_WRONG_DATA();
+ mbedtls_ecp_group_id ec_id = ec_get_curve_from_prime(prime, prime_len);
+ printf("KEYPAIR ECC %d\r\n",ec_id);
+ if (ec_id == MBEDTLS_ECP_DP_NONE) {
+ 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) {
+ mbedtls_ecdsa_free(&ecdsa);
+ return SW_EXEC_ERROR();
+ }
+ size_t l91 = 0, ext_len = 0;
+ uint8_t *p91 = NULL, *ext = NULL;
+ if (asn1_find_tag(apdu.data, apdu.nc, 0x91, &l91, &p91) && p91 != NULL && l91 > 0) {
+ for (int n = 0; n < l91; n++) {
+ if (p91[n] == ALGO_EC_DH_XKEK) {
+ size_t l92 = 0;
+ uint8_t *p92 = NULL;
+ if (!asn1_find_tag(apdu.data, apdu.nc, 0x92, &l92, &p92) || p92 == NULL || l92 == 0)
+ return SW_WRONG_DATA();
+ if (p92[0] > MAX_KEY_DOMAINS)
+ return SW_WRONG_DATA();
+ file_t *tf_xkek = search_dynamic_file(EF_XKEK+p92[0]);
+ if (!tf_xkek)
+ return SW_WRONG_DATA();
+ ext_len = 2+2+strlen(OID_ID_KEY_DOMAIN_UID)+2+file_get_size(tf_xkek);
+ ext = (uint8_t *)calloc(1, ext_len);
+ uint8_t *pe = ext;
+ *pe++ = 0x73;
+ *pe++ = ext_len-2;
+ *pe++ = 0x6;
+ *pe++ = strlen(OID_ID_KEY_DOMAIN_UID);
+ memcpy(pe, OID_ID_KEY_DOMAIN_UID, strlen(OID_ID_KEY_DOMAIN_UID));
+ pe += strlen(OID_ID_KEY_DOMAIN_UID);
+ *pe++ = 0x80;
+ *pe++ = file_get_size(tf_xkek);
+ memcpy(pe, file_get_data(tf_xkek), file_get_size(tf_xkek));
+ }
+ }
+ }
+ if ((res_APDU_size = asn1_cvc_aut(&ecdsa, HSM_KEY_EC, res_APDU, 4096, ext, ext_len)) == 0) {
+ return SW_EXEC_ERROR();
+ }
+ ret = store_keys(&ecdsa, HSM_KEY_EC, key_id);
+ if (ret != CCID_OK) {
+ mbedtls_ecdsa_free(&ecdsa);
+ return SW_EXEC_ERROR();
+ }
+ mbedtls_ecdsa_free(&ecdsa);
+ }
+
+ }
+ }
+ else
+ return SW_WRONG_DATA();
+ if (find_and_store_meta_key(key_id) != CCID_OK)
+ return SW_EXEC_ERROR();
+ 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();
+ if (apdu.ne == 0)
+ apdu.ne = res_APDU_size;
+ low_flash_available();
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_list_keys.c b/src/hsm/cmd_list_keys.c
new file mode 100644
index 0000000..7ecb6e6
--- /dev/null
+++ b/src/hsm/cmd_list_keys.c
@@ -0,0 +1,49 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+
+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++] = PRKD_PREFIX;
+ res_APDU[res_APDU_size++] = f->fid & 0xff;
+ 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;
+ }
+ }
+
+ for (int i = 0; i < dynamic_files; i++) {
+ file_t *f = &dynamic_file[i];
+ if ((f->fid & 0xff00) == (DCOD_PREFIX << 8)) {
+ res_APDU[res_APDU_size++] = DCOD_PREFIX;
+ res_APDU[res_APDU_size++] = f->fid & 0xff;
+ }
+ }
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_mse.c b/src/hsm/cmd_mse.c
new file mode 100644
index 0000000..8127e58
--- /dev/null
+++ b/src/hsm/cmd_mse.c
@@ -0,0 +1,77 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+#include "asn1.h"
+#include "oid.h"
+#include "eac.h"
+#include "files.h"
+#include "cvc.h"
+
+file_t *ef_puk_aut = NULL;
+
+int cmd_mse() {
+ int p1 = P1(apdu);
+ int p2 = P2(apdu);
+ if (p2 != 0xA4 && p2 != 0xA6 && p2 != 0xAA && p2 != 0xB4 && p2 != 0xB6 && p2 != 0xB8)
+ return SW_INCORRECT_P1P2();
+ if (p1 & 0x1) { //SET
+ uint16_t tag = 0x0;
+ uint8_t *tag_data = NULL, *p = NULL;
+ size_t tag_len = 0;
+ while (walk_tlv(apdu.data, apdu.nc, &p, &tag, &tag_len, &tag_data)) {
+ if (tag == 0x80) {
+ if (p2 == 0xA4) {
+ if (tag_len == 10 && memcmp(tag_data, OID_ID_CA_ECDH_AES_CBC_CMAC_128, tag_len) == 0)
+ sm_set_protocol(MSE_AES);
+ else if (tag_len == 10 && memcmp(tag_data, OID_ID_CA_ECDH_3DES_CBC_CBC, tag_len) == 0)
+ sm_set_protocol(MSE_3DES);
+ }
+ }
+ else if (tag == 0x83) {
+ if (tag_len == 1) {
+
+ }
+ else {
+ if (p2 == 0xB6) {
+ if (puk_store_select_chr(tag_data) == CCID_OK)
+ return SW_OK();
+ }
+ else if (p2 == 0xA4) { /* Aut */
+ for (int i = 0; i < MAX_PUK; i++) {
+ file_t *ef = search_dynamic_file(EF_PUK+i);
+ if (!ef)
+ break;
+ if (ef->data == NULL || file_get_size(ef) == 0)
+ break;
+ size_t chr_len = 0;
+ const uint8_t *chr = cvc_get_chr(file_get_data(ef), file_get_size(ef), &chr_len);
+ if (memcmp(chr, tag_data, chr_len) == 0) {
+ ef_puk_aut = ef;
+ return SW_OK();
+ }
+ }
+ }
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ }
+ }
+ }
+ else
+ return SW_INCORRECT_P1P2();
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_pso.c b/src/hsm/cmd_pso.c
new file mode 100644
index 0000000..8318b95
--- /dev/null
+++ b/src/hsm/cmd_pso.c
@@ -0,0 +1,136 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+#include "oid.h"
+#include "asn1.h"
+#include "cvc.h"
+
+extern int add_cert_puk_store(const uint8_t *data, size_t data_len, bool copy);
+extern PUK *current_puk;
+
+int cmd_pso() {
+ uint8_t p1 = P1(apdu), p2 = P2(apdu);
+ if (p1 == 0x0 && (p2 == 0x92 || p2 == 0xAE || p2 == 0xBE)) { /* Verify certificate */
+ if (apdu.nc == 0)
+ return SW_WRONG_LENGTH();
+ if (current_puk == NULL)
+ return SW_REFERENCE_NOT_FOUND();
+ if (apdu.data[0] != 0x7F || apdu.data[1] != 0x21) {
+ uint8_t tlv_len = 2+format_tlv_len(apdu.nc, NULL);
+ memmove(apdu.data+tlv_len, apdu.data, apdu.nc);
+ memcpy(apdu.data, "\x7F\x21", 2);
+ format_tlv_len(apdu.nc, apdu.data+2);
+ apdu.nc += tlv_len;
+ }
+ int r = cvc_verify(apdu.data, apdu.nc, current_puk->cvcert, current_puk->cvcert_len);
+ if (r != CCID_OK) {
+ if (r == CCID_WRONG_DATA)
+ return SW_DATA_INVALID();
+ else if (r == CCID_WRONG_SIGNATURE)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ return SW_EXEC_ERROR();
+ }
+ for (int i = 0; i < 0xfe; i++) {
+ uint16_t fid = (CA_CERTIFICATE_PREFIX << 8) | i;
+ file_t *ca_ef = search_dynamic_file(fid);
+ if (!ca_ef) {
+ ca_ef = file_new(fid);
+ flash_write_data_to_file(ca_ef, apdu.data, apdu.nc);
+ if (add_cert_puk_store(file_get_data(ca_ef), file_get_size(ca_ef), false) != CCID_OK)
+ return SW_FILE_FULL();
+
+ size_t chr_len = 0;
+ const uint8_t *chr = cvc_get_chr(apdu.data, apdu.nc, &chr_len);
+ if (chr == NULL)
+ return SW_WRONG_DATA();
+ size_t puk_len = 0, puk_bin_len = 0;
+ const uint8_t *puk = cvc_get_pub(apdu.data, apdu.nc, &puk_len), *puk_bin = NULL;
+ if (puk == NULL)
+ return SW_WRONG_DATA();
+ size_t oid_len = 0;
+ const uint8_t *oid = cvc_get_field(puk, puk_len, &oid_len, 0x6);
+ if (oid == NULL)
+ return SW_WRONG_DATA();
+ if (memcmp(oid, OID_ID_TA_RSA, 9) == 0) { //RSA
+ puk_bin = cvc_get_field(puk, puk_len, &puk_bin_len, 0x81);
+ if (!puk_bin)
+ return SW_WRONG_DATA();
+ }
+ else if (memcmp(oid, OID_ID_TA_ECDSA, 9) == 0) { //ECC
+ mbedtls_ecp_group_id ec_id = cvc_inherite_ec_group(apdu.data, apdu.nc);
+ mbedtls_ecp_group grp;
+ mbedtls_ecp_group_init(&grp);
+ if (mbedtls_ecp_group_load(&grp, ec_id) != 0) {
+ mbedtls_ecp_group_free(&grp);
+ return SW_WRONG_DATA();
+ }
+ size_t plen = mbedtls_mpi_size(&grp.P);
+ size_t t86_len = 0;
+ const uint8_t *t86 = cvc_get_field(puk, puk_len, &t86_len, 0x86);
+ if (mbedtls_ecp_get_type(&grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
+ if (plen != t86_len) {
+ mbedtls_ecp_group_free(&grp);
+ return SW_WRONG_DATA();
+ }
+ puk_bin = t86;
+ puk_bin_len = t86_len;
+ }
+ else if (mbedtls_ecp_get_type(&grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
+ if (t86[0] == 0x2 || t86[0] == 0x3) {
+ if (t86_len != plen+1) {
+ mbedtls_ecp_group_free(&grp);
+ return SW_WRONG_DATA();
+ }
+ }
+ else if (t86[0] == 0x4) {
+ if (t86_len != 2*plen+1) {
+ mbedtls_ecp_group_free(&grp);
+ return SW_WRONG_DATA();
+ }
+ }
+ else {
+ mbedtls_ecp_group_free(&grp);
+ return SW_WRONG_DATA();
+ }
+ puk_bin = t86+1;
+ puk_bin_len = plen;
+ }
+ mbedtls_ecp_group_free(&grp);
+ if (!puk_bin)
+ return SW_WRONG_DATA();
+ }
+ file_t *cd_ef = file_new((CD_PREFIX << 8) | i);
+ size_t cd_len = asn1_build_cert_description(chr, chr_len, puk_bin, puk_bin_len, fid, NULL, 0);
+ if (cd_len == 0)
+ return SW_EXEC_ERROR();
+ uint8_t *buf = (uint8_t *)calloc(cd_len, sizeof(uint8_t));
+ int r = asn1_build_cert_description(chr, chr_len, puk_bin, puk_bin_len, fid, buf, cd_len);
+ flash_write_data_to_file(cd_ef, buf, cd_len);
+ free(buf);
+ if (r == 0)
+ return SW_EXEC_ERROR();
+ low_flash_available();
+ break;
+ }
+ }
+ return SW_OK();
+ }
+ else
+ return SW_INCORRECT_P1P2();
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_puk_auth.c b/src/hsm/cmd_puk_auth.c
new file mode 100644
index 0000000..f3e5e3d
--- /dev/null
+++ b/src/hsm/cmd_puk_auth.c
@@ -0,0 +1,85 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+#include "files.h"
+#include "cvc.h"
+
+int cmd_puk_auth() {
+ uint8_t p1 = P1(apdu), p2 = P2(apdu);
+ file_t *ef_puk = search_by_fid(EF_PUKAUT, NULL, SPECIFY_EF);
+ if (!ef_puk || !ef_puk->data || file_get_size(ef_puk) == 0)
+ return SW_FILE_NOT_FOUND();
+ uint8_t *puk_data = file_get_data(ef_puk);
+ if (apdu.nc > 0) {
+ if (p1 == 0x0 || p1 == 0x1) {
+ file_t *ef = NULL;
+ if (p1 == 0x0) { /* Add */
+ if (p2 != 0x0)
+ return SW_INCORRECT_P1P2();
+ for (int i = 0; i < puk_data[0]; i++) {
+ ef = search_dynamic_file(EF_PUK+i);
+ if (!ef) /* Never should not happen */
+ return SW_MEMORY_FAILURE();
+ if (ef->data == NULL || file_get_size(ef) == 0) /* found first empty slot */
+ break;
+ }
+ uint8_t *tmp = (uint8_t *)calloc(file_get_size(ef_puk), sizeof(uint8_t));
+ memcpy(tmp, puk_data, file_get_size(ef_puk));
+ tmp[1] = puk_data[1]-1;
+ flash_write_data_to_file(ef_puk, tmp, file_get_size(ef_puk));
+ puk_data = file_get_data(ef_puk);
+ free(tmp);
+ }
+ else if (p1 == 0x1) { /* Replace */
+ if (p2 >= puk_data[0])
+ return SW_INCORRECT_P1P2();
+ ef = search_dynamic_file(EF_PUK+p2);
+ if (!ef) /* Never should not happen */
+ return SW_MEMORY_FAILURE();
+ }
+ flash_write_data_to_file(ef, apdu.data, apdu.nc);
+ low_flash_available();
+ }
+ else
+ return SW_INCORRECT_P1P2();
+ }
+ if (p1 == 0x2) {
+ if (p2 >= puk_data[0])
+ return SW_INCORRECT_P1P2();
+ file_t *ef = search_dynamic_file(EF_PUK+p2);
+ if (!ef)
+ return SW_INCORRECT_P1P2();
+ if (ef->data == NULL || file_get_size(ef) == 0)
+ return SW_REFERENCE_NOT_FOUND();
+ size_t chr_len = 0;
+ const uint8_t *chr = cvc_get_chr(file_get_data(ef), file_get_size(ef), &chr_len);
+ if (chr) {
+ memcpy(res_APDU, chr, chr_len);
+ res_APDU_size = chr_len;
+ }
+ return set_res_sw(0x90, puk_status[p2]);
+ }
+ else {
+ memcpy(res_APDU, puk_data, 3);
+ res_APDU[3] = 0;
+ for (int i = 0; i < puk_data[0]; i++)
+ res_APDU[3] += puk_status[i];
+ res_APDU_size = 4;
+ }
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_read_binary.c b/src/hsm/cmd_read_binary.c
new file mode 100644
index 0000000..1767b95
--- /dev/null
+++ b/src/hsm/cmd_read_binary.c
@@ -0,0 +1,90 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+
+int cmd_read_binary() {
+ uint16_t fid = 0x0;
+ uint32_t offset = 0;
+ 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.data[0] != 0x54)
+ return SW_WRONG_DATA();
+
+ offset = 0;
+ for (int d = 0; d < apdu.data[1]; d++)
+ offset |= apdu.data[2+d]<<(apdu.data[1]-1-d)*8;
+ }
+ }
+
+ if ((fid >> 8) == KEY_PREFIX || !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.ne > maxle)
+ apdu.ne = maxle;
+ if (offset) {
+ memmove(res_APDU, res_APDU+offset, res_APDU_size-offset);
+ //res_APDU += offset;
+ res_APDU_size -= offset;
+ }
+ }
+ else {
+ uint16_t data_len = file_get_size(ef);
+ if (offset > data_len)
+ return SW_WRONG_P1P2();
+
+ uint16_t maxle = data_len-offset;
+ if (apdu.ne > maxle)
+ apdu.ne = maxle;
+ memcpy(res_APDU, file_get_data(ef)+offset, data_len-offset);
+ res_APDU_size = data_len-offset;
+ }
+ }
+
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_reset_retry.c b/src/hsm/cmd_reset_retry.c
new file mode 100644
index 0000000..4dff5f6
--- /dev/null
+++ b/src/hsm/cmd_reset_retry.c
@@ -0,0 +1,95 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "kek.h"
+
+int cmd_reset_retry() {
+ if (P2(apdu) != 0x81)
+ return SW_REFERENCE_NOT_FOUND();
+ if (!file_sopin || !file_pin1) {
+ return SW_FILE_NOT_FOUND();
+ }
+ if (!file_sopin->data) {
+ return SW_REFERENCE_NOT_FOUND();
+ }
+ uint16_t opts = get_device_options();
+ if (!(opts & HSM_OPT_RRC))
+ return SW_COMMAND_NOT_ALLOWED();
+ if (P1(apdu) == 0x0 || P1(apdu) == 0x2) {
+ int newpin_len = 0;
+ if (P1(apdu) == 0x0) {
+ if (apdu.nc <= 8)
+ return SW_WRONG_LENGTH();
+ uint16_t r = check_pin(file_sopin, apdu.data, 8);
+ if (r != 0x9000)
+ return r;
+ newpin_len = apdu.nc-8;
+ has_session_sopin = true;
+ hash_multi(apdu.data, 8, session_sopin);
+ }
+ else if (P1(apdu) == 0x2) {
+ if (!has_session_sopin)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ if (apdu.nc > 16)
+ return SW_WRONG_LENGTH();
+ newpin_len = apdu.nc;
+ }
+ uint8_t dhash[33];
+ dhash[0] = newpin_len;
+ double_hash_pin(apdu.data+(apdu.nc-newpin_len), newpin_len, dhash+1);
+ flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
+ if (pin_reset_retries(file_pin1, true) != CCID_OK)
+ return SW_MEMORY_FAILURE();
+ uint8_t mkek[MKEK_SIZE];
+ int r = load_mkek(mkek); //loads the MKEK with SO pin
+ if (r != CCID_OK)
+ return SW_EXEC_ERROR();
+ hash_multi(apdu.data+(apdu.nc-newpin_len), newpin_len, session_pin);
+ has_session_pin = true;
+ r = store_mkek(mkek);
+ release_mkek(mkek);
+ if (r != CCID_OK)
+ return SW_EXEC_ERROR();
+ low_flash_available();
+ return SW_OK();
+ }
+ else if (P1(apdu) == 0x1 || P1(apdu) == 0x3) {
+ if (!(opts & HSM_OPT_RRC_RESET_ONLY))
+ return SW_COMMAND_NOT_ALLOWED();
+ if (P1(apdu) == 0x1) {
+ if (apdu.nc != 8)
+ return SW_WRONG_LENGTH();
+ uint16_t r = check_pin(file_sopin, apdu.data, 8);
+ if (r != 0x9000)
+ return r;
+ has_session_sopin = true;
+ hash_multi(apdu.data, 8, session_sopin);
+ }
+ else if (P1(apdu) == 0x3) {
+ if (!has_session_sopin)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ if (apdu.nc != 0)
+ return SW_WRONG_LENGTH();
+ }
+ if (pin_reset_retries(file_pin1, true) != CCID_OK)
+ return SW_MEMORY_FAILURE();
+ return SW_OK();
+ }
+ return SW_INCORRECT_P1P2();
+}
diff --git a/src/hsm/cmd_select.c b/src/hsm/cmd_select.c
new file mode 100644
index 0000000..0ec247a
--- /dev/null
+++ b/src/hsm/cmd_select.c
@@ -0,0 +1,132 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+#include "version.h"
+
+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
+ }
+}
+
+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.nc >= 2)
+ fid = get_uint16_t(apdu.data, 0);
+
+ //if ((fid & 0xff00) == (KEY_PREFIX << 8))
+ // fid = (PRKD_PREFIX << 8) | (fid & 0xff);
+
+ uint8_t pfx = fid >> 8;
+ if (pfx == PRKD_PREFIX ||
+ pfx == CD_PREFIX ||
+ pfx == CA_CERTIFICATE_PREFIX ||
+ pfx == KEY_PREFIX ||
+ pfx == EE_CERTIFICATE_PREFIX ||
+ pfx == DCOD_PREFIX ||
+ pfx == DATA_PREFIX ||
+ pfx == PROT_DATA_PREFIX) {
+ 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.nc == 0) {
+ pe = (file_t *)MF;
+ //ac_fini();
+ }
+ else if (apdu.nc == 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.nc != 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.data, apdu.nc))) {
+ 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.data, apdu.nc, 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.data, apdu.nc, currentDF))) {
+ return SW_FILE_NOT_FOUND();
+ }
+ }
+ }
+ if ((p2 & 0xfc) == 0x00 || (p2 & 0xfc) == 0x04) {
+ process_fci(pe,0);
+ if (pe == file_sc_hsm) {
+ res_APDU[res_APDU_size++] = 0x85;
+ res_APDU[res_APDU_size++] = 5;
+ uint16_t opts = get_device_options();
+ res_APDU[res_APDU_size++] = opts >> 8;
+ res_APDU[res_APDU_size++] = opts & 0xff;
+ res_APDU[res_APDU_size++] = 0xFF;
+ res_APDU[res_APDU_size++] = HSM_VERSION_MAJOR;
+ res_APDU[res_APDU_size++] = HSM_VERSION_MINOR;
+ res_APDU[1] = res_APDU_size-2;
+ }
+ }
+ else
+ return SW_INCORRECT_P1P2();
+ select_file(pe);
+ return SW_OK ();
+}
diff --git a/src/hsm/cmd_session_pin.c b/src/hsm/cmd_session_pin.c
new file mode 100644
index 0000000..cd950f1
--- /dev/null
+++ b/src/hsm/cmd_session_pin.c
@@ -0,0 +1,34 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+#include "random.h"
+#include "eac.h"
+
+int cmd_session_pin() {
+ if (P1(apdu) == 0x01 && P2(apdu) == 0x81) {
+ memcpy(sm_session_pin, random_bytes_get(8), 8);
+ sm_session_pin_len = 8;
+
+ memcpy(res_APDU, sm_session_pin, sm_session_pin_len);
+ res_APDU_size = sm_session_pin_len;
+ apdu.ne = sm_session_pin_len;
+ }
+ else
+ return SW_INCORRECT_P1P2();
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_signature.c b/src/hsm/cmd_signature.c
new file mode 100644
index 0000000..ce0328c
--- /dev/null
+++ b/src/hsm/cmd_signature.c
@@ -0,0 +1,241 @@
+/*
+ * 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 .
+ */
+
+#include "crypto_utils.h"
+#include "sc_hsm.h"
+#include "asn1.h"
+#include "mbedtls/oid.h"
+#include "random.h"
+
+//-----
+/* From OpenSC */
+static const uint8_t hdr_md5[] = {
+ 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10
+};
+static const uint8_t hdr_sha1[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
+ 0x05, 0x00, 0x04, 0x14
+};
+static const uint8_t hdr_sha256[] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
+};
+static const uint8_t hdr_sha384[] = {
+ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30
+};
+static const uint8_t hdr_sha512[] = {
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40
+};
+static const uint8_t hdr_sha224[] = {
+ 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c
+};
+static const uint8_t hdr_ripemd160[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01,
+ 0x05, 0x00, 0x04, 0x14
+};
+static const struct digest_info_prefix {
+ mbedtls_md_type_t algorithm;
+ const uint8_t * hdr;
+ size_t hdr_len;
+ size_t hash_len;
+} digest_info_prefix[] = {
+ { MBEDTLS_MD_MD5, hdr_md5, sizeof(hdr_md5), 16 },
+ { MBEDTLS_MD_SHA1, hdr_sha1, sizeof(hdr_sha1), 20 },
+ { MBEDTLS_MD_SHA256, hdr_sha256, sizeof(hdr_sha256), 32 },
+ { MBEDTLS_MD_SHA384, hdr_sha384, sizeof(hdr_sha384), 48 },
+ { MBEDTLS_MD_SHA512, hdr_sha512, sizeof(hdr_sha512), 64 },
+ { MBEDTLS_MD_SHA224, hdr_sha224, sizeof(hdr_sha224), 28 },
+ { MBEDTLS_MD_RIPEMD160,hdr_ripemd160, sizeof(hdr_ripemd160), 20 },
+ { 0, NULL, 0, 0 }
+};
+int pkcs1_strip_digest_info_prefix(mbedtls_md_type_t *algorithm, const uint8_t *in_dat, size_t in_len, uint8_t *out_dat, size_t *out_len)
+{
+ for (int i = 0; digest_info_prefix[i].algorithm != 0; i++) {
+ size_t hdr_len = digest_info_prefix[i].hdr_len, hash_len = digest_info_prefix[i].hash_len;
+ const uint8_t *hdr = digest_info_prefix[i].hdr;
+ if (in_len == (hdr_len + hash_len) && !memcmp(in_dat, hdr, hdr_len)) {
+ if (algorithm)
+ *algorithm = digest_info_prefix[i].algorithm;
+ if (out_dat == NULL)
+ return CCID_OK;
+ if (*out_len < hash_len)
+ return CCID_WRONG_DATA;
+ memmove(out_dat, in_dat + hdr_len, hash_len);
+ *out_len = hash_len;
+ return CCID_OK;
+ }
+ }
+ return CCID_EXEC_ERROR;
+}
+//-----
+
+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 || file_get_size(fkey) == 0)
+ return SW_FILE_NOT_FOUND();
+ if (get_key_counter(fkey) == 0)
+ return SW_FILE_FULL();
+ if (key_has_purpose(fkey, p2) == false)
+ return SW_CONDITIONS_NOT_SATISFIED();
+ int key_size = file_get_size(fkey);
+ 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.data, apdu.nc, apdu.data);
+ apdu.nc = mbedtls_md_get_size(mbedtls_md_info_from_type(md));
+ }
+ if (p2 >= ALGO_RSA_RAW && p2 <= ALGO_RSA_PSS_SHA512) {
+ mbedtls_rsa_context ctx;
+ mbedtls_rsa_init(&ctx);
+
+ int r;
+ r = load_private_key_rsa(&ctx, fkey);
+ if (r != CCID_OK) {
+ mbedtls_rsa_free(&ctx);
+ if (r == CCID_VERIFICATION_FAILED)
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+ return SW_EXEC_ERROR();
+ }
+ uint8_t *hash = apdu.data;
+ size_t hash_len = apdu.nc;
+ if (p2 == ALGO_RSA_PKCS1) { //DigestInfo attached
+ size_t nc = apdu.nc;
+ if (pkcs1_strip_digest_info_prefix(&md, apdu.data, apdu.nc, apdu.data, &nc) != CCID_OK) //gets the MD algo id and strips it off
+ return SW_EXEC_ERROR();
+ apdu.nc = nc;
+ }
+ else {
+ //sc_asn1_print_tags(apdu.data, apdu.nc);
+ size_t tout = 0, oid_len = 0;
+ uint8_t *p = NULL, *oid = NULL;
+ if (asn1_find_tag(apdu.data, apdu.nc, 0x30, &tout, &p) && tout > 0 && p != NULL) {
+ size_t tout30 = 0;
+ uint8_t *c30 = NULL;
+ if (asn1_find_tag(p, tout, 0x30, &tout30, &c30) && tout30 > 0 && c30 != NULL) {
+ asn1_find_tag(c30, tout30, 0x6, &oid_len, &oid);
+ }
+ asn1_find_tag(p, tout, 0x4, &hash_len, &hash);
+ }
+ if (oid && oid_len > 0) {
+ if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA1, oid_len) == 0)
+ md = MBEDTLS_MD_SHA1;
+ else if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA224, oid_len) == 0)
+ md = MBEDTLS_MD_SHA224;
+ else if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA256, oid_len) == 0)
+ md = MBEDTLS_MD_SHA256;
+ else if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA384, oid_len) == 0)
+ md = MBEDTLS_MD_SHA384;
+ else if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA512, oid_len) == 0)
+ md = MBEDTLS_MD_SHA512;
+ }
+ if (p2 >= ALGO_RSA_PSS && p2 <= ALGO_RSA_PSS_SHA512) {
+ if (p2 == ALGO_RSA_PSS && !oid) {
+ if (apdu.nc == 20) //default is sha1
+ md = MBEDTLS_MD_SHA1;
+ else if (apdu.nc == 28)
+ md = MBEDTLS_MD_SHA224;
+ else if (apdu.nc == 32)
+ md = MBEDTLS_MD_SHA256;
+ else if (apdu.nc == 48)
+ md = MBEDTLS_MD_SHA384;
+ else if (apdu.nc == 64)
+ md = MBEDTLS_MD_SHA512;
+ }
+ mbedtls_rsa_set_padding(&ctx, MBEDTLS_RSA_PKCS_V21, md);
+ }
+ }
+ if (md == MBEDTLS_MD_NONE) {
+ if (apdu.nc < key_size) //needs padding
+ memset(apdu.data+apdu.nc, 0, key_size-apdu.nc);
+ r = mbedtls_rsa_private(&ctx, random_gen, NULL, 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.ne = key_size;
+ mbedtls_rsa_free(&ctx);
+ }
+ else if (p2 >= ALGO_EC_RAW && p2 <= ALGO_EC_SHA512) {
+ mbedtls_ecdsa_context ctx;
+ mbedtls_ecdsa_init(&ctx);
+ md = MBEDTLS_MD_SHA256;
+ if (p2 == ALGO_EC_RAW) {
+ if (apdu.nc == 32)
+ md = MBEDTLS_MD_SHA256;
+ else if (apdu.nc == 20)
+ md = MBEDTLS_MD_SHA1;
+ else if (apdu.nc == 28)
+ md = MBEDTLS_MD_SHA224;
+ else if (apdu.nc == 48)
+ md = MBEDTLS_MD_SHA384;
+ else if (apdu.nc == 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;
+ else if (p2 == ALGO_EC_SHA384)
+ md = MBEDTLS_MD_SHA384;
+ else if (p2 == ALGO_EC_SHA512)
+ md = MBEDTLS_MD_SHA512;
+ int r;
+ r = load_private_key_ecdsa(&ctx, fkey);
+ if (r != CCID_OK) {
+ mbedtls_ecdsa_free(&ctx);
+ if (r == CCID_VERIFICATION_FAILED)
+ return SW_SECURE_MESSAGE_EXEC_ERROR();
+ return SW_EXEC_ERROR();
+ }
+ size_t olen = 0;
+ uint8_t buf[MBEDTLS_ECDSA_MAX_LEN];
+ if (mbedtls_ecdsa_write_signature(&ctx, md, apdu.data, apdu.nc, 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();
+ decrement_key_counter(fkey);
+ return SW_OK();
+}
diff --git a/src/hsm/cmd_update_ef.c b/src/hsm/cmd_update_ef.c
new file mode 100644
index 0000000..236d5e7
--- /dev/null
+++ b/src/hsm/cmd_update_ef.c
@@ -0,0 +1,86 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+#include "asn1.h"
+
+extern void select_file(file_t *pe);
+
+int cmd_update_ef() {
+ uint8_t p1 = P1(apdu), p2 = P2(apdu);
+ uint16_t fid = (p1 << 8) | p2;
+ uint8_t *data = NULL;
+ 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 && p1 != DATA_PREFIX && p1 != DCOD_PREFIX && p1 != PROT_DATA_PREFIX)
+ return SW_INCORRECT_P1P2();
+
+ if (ef && !authenticate_action(ef, ACL_OP_UPDATE_ERASE))
+ return SW_SECURITY_STATUS_NOT_SATISFIED();
+
+ uint16_t tag = 0x0;
+ uint8_t *tag_data = NULL, *p = NULL;
+ size_t tag_len = 0;
+ while (walk_tlv(apdu.data, apdu.nc, &p, &tag, &tag_len, &tag_data)) {
+ if (tag == 0x54) { //ofset tag
+ for (int i = 1; i <= tag_len; i++)
+ offset |= (*tag_data++ << (8*(tag_len-i)));
+ }
+ else if (tag == 0x53) { //data
+ data_len = tag_len;
+ data = tag_data;
+ }
+ }
+ 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 != CCID_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_get_data(ef), offset);
+ memcpy(data_merge+offset, data, data_len);
+ int r = flash_write_data_to_file(ef, data_merge, offset+data_len);
+ free(data_merge);
+ if (r != CCID_OK)
+ return SW_MEMORY_FAILURE();
+ }
+ low_flash_available();
+ }
+ return SW_OK();
+}
\ No newline at end of file
diff --git a/src/hsm/cmd_verify.c b/src/hsm/cmd_verify.c
new file mode 100644
index 0000000..616b4a8
--- /dev/null
+++ b/src/hsm/cmd_verify.c
@@ -0,0 +1,58 @@
+/*
+ * 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 .
+ */
+
+#include "sc_hsm.h"
+
+int cmd_verify() {
+ uint8_t p1 = P1(apdu);
+ uint8_t p2 = P2(apdu);
+
+ if (p1 != 0x0 || (p2 & 0x60) != 0x0)
+ return SW_WRONG_P1P2();
+
+ if (p2 == 0x81) { //UserPin
+ uint16_t opts = get_device_options();
+ if (opts & HSM_OPT_TRANSPORT_PIN)
+ return SW_DATA_INVALID();
+ if (has_session_pin && apdu.nc == 0)
+ return SW_OK();
+ if (*file_get_data(file_pin1) == 0 && pka_enabled() == false) //not initialized
+ return SW_REFERENCE_NOT_FOUND();
+ if (apdu.nc > 0) {
+ return check_pin(file_pin1, apdu.data, apdu.nc);
+ }
+ if (file_read_uint8(file_get_data(file_retries_pin1)) == 0)
+ return SW_PIN_BLOCKED();
+ return set_res_sw(0x63, 0xc0 | file_read_uint8(file_get_data(file_retries_pin1)));
+ }
+ else if (p2 == 0x88) { //SOPin
+ if (file_read_uint8(file_get_data(file_sopin)) == 0) //not initialized
+ return SW_REFERENCE_NOT_FOUND();
+ if (apdu.nc > 0) {
+ return check_pin(file_sopin, apdu.data, apdu.nc);
+ }
+ if (file_read_uint8(file_get_data(file_retries_sopin)) == 0)
+ return SW_PIN_BLOCKED();
+ if (has_session_sopin)
+ return SW_OK();
+ return set_res_sw(0x63, 0xc0 | file_read_uint8(file_get_data(file_retries_sopin)));
+ }
+ else if (p2 == 0x85) {
+ return SW_OK();
+ }
+ return SW_REFERENCE_NOT_FOUND();
+}
\ No newline at end of file
diff --git a/src/hsm/cvcerts.h b/src/hsm/cvcerts.h
new file mode 100644
index 0000000..14bd8fe
--- /dev/null
+++ b/src/hsm/cvcerts.h
@@ -0,0 +1,74 @@
+#ifndef _CVCERTS_H_
+#define _CVCERTS_H_
+
+static const unsigned char cvca[] = {
+ 0x6a,0x01,
+ 0x7f,0x21,0x82,0x01,0x65,0x7f,0x4e,0x82,0x01,0x2d,0x5f,0x29,0x01,0x00,0x42,0x0e,
+ 0x45,0x53,0x43,0x56,0x43,0x41,0x48,0x53,0x4d,0x30,0x30,0x30,0x30,0x31,0x7f,0x49,
+ 0x81,0xdd,0x06,0x0a,0x04,0x00,0x7f,0x00,0x07,0x02,0x02,0x02,0x02,0x03,0x81,0x18,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x82,0x18,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xff,0xfc,0x83,0x18,0x64,0x21,0x05,0x19,0xe5,0x9c,0x80,0xe7,0x0f,0xa7,0xe9,0xab,
+ 0x72,0x24,0x30,0x49,0xfe,0xb8,0xde,0xec,0xc1,0x46,0xb9,0xb1,0x84,0x31,0x04,0x18,
+ 0x8d,0xa8,0x0e,0xb0,0x30,0x90,0xf6,0x7c,0xbf,0x20,0xeb,0x43,0xa1,0x88,0x00,0xf4,
+ 0xff,0x0a,0xfd,0x82,0xff,0x10,0x12,0x07,0x19,0x2b,0x95,0xff,0xc8,0xda,0x78,0x63,
+ 0x10,0x11,0xed,0x6b,0x24,0xcd,0xd5,0x73,0xf9,0x77,0xa1,0x1e,0x79,0x48,0x11,0x85,
+ 0x18,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x99,0xde,0xf8,
+ 0x36,0x14,0x6b,0xc9,0xb1,0xb4,0xd2,0x28,0x31,0x86,0x31,0x04,0x08,0x8f,0xcd,0xfc,
+ 0xce,0x87,0xed,0xd2,0x85,0x92,0x06,0x15,0xe6,0x51,0xd7,0x64,0x52,0xd8,0x57,0xec,
+ 0xbb,0x40,0x8c,0x32,0x7a,0xdb,0x48,0xa2,0xa5,0x14,0xc1,0xc9,0xbd,0x77,0xcc,0x97,
+ 0x83,0x60,0x7a,0x74,0x14,0x93,0xa7,0x42,0x74,0x4a,0xd1,0x73,0x87,0x01,0x01,0x5f,
+ 0x20,0x0e,0x45,0x53,0x43,0x56,0x43,0x41,0x48,0x53,0x4d,0x30,0x30,0x30,0x30,0x31,
+ 0x7f,0x4c,0x12,0x06,0x09,0x04,0x00,0x7f,0x00,0x07,0x03,0x01,0x02,0x02,0x53,0x05,
+ 0xc0,0x00,0x00,0x00,0x04,0x5f,0x25,0x06,0x02,0x02,0x00,0x03,0x02,0x06,0x5f,0x24,
+ 0x06,0x03,0x00,0x01,0x02,0x03,0x01,0x5f,0x37,0x30,0x72,0x97,0x77,0x76,0x64,0xb6,
+ 0x0c,0x57,0xa2,0xc4,0x5e,0x7b,0xfd,0x12,0xe5,0x20,0x14,0x3e,0xde,0x90,0x38,0xbf,
+ 0xb3,0x02,0x73,0x91,0x06,0xf2,0x73,0x0d,0x76,0x06,0x65,0xd7,0x46,0x49,0x91,0x0c,
+ 0x51,0x90,0x89,0x84,0x8d,0x4f,0xb6,0xe5,0x13,0x40
+};
+
+static const unsigned char dica[] = {
+ 0xc9,0x00,
+ 0x7f,0x21,0x81,0xc5,0x7f,0x4e,0x81,0x8e,0x5f,0x29,0x01,0x00,0x42,0x0e,0x45,0x53,
+ 0x43,0x56,0x43,0x41,0x48,0x53,0x4d,0x30,0x30,0x30,0x30,0x31,0x7f,0x49,0x3f,0x06,
+ 0x0a,0x04,0x00,0x7f,0x00,0x07,0x02,0x02,0x02,0x02,0x03,0x86,0x31,0x04,0x93,0x7e,
+ 0xdf,0xf1,0xa6,0xd2,0x40,0x7e,0xb4,0x71,0xb2,0x97,0x50,0xdb,0x7e,0xe1,0x70,0xfb,
+ 0x6c,0xcd,0x06,0x47,0x2a,0x3e,0x9c,0x8d,0x59,0x56,0x57,0xbe,0x11,0x11,0x0a,0x08,
+ 0x81,0x54,0xed,0x22,0xc0,0x83,0xac,0xa1,0x2e,0x39,0x7b,0xd4,0x65,0x1f,0x5f,0x20,
+ 0x0e,0x45,0x53,0x44,0x56,0x43,0x41,0x48,0x53,0x4d,0x30,0x30,0x30,0x30,0x31,0x7f,
+ 0x4c,0x12,0x06,0x09,0x04,0x00,0x7f,0x00,0x07,0x03,0x01,0x02,0x02,0x53,0x05,0x80,
+ 0x00,0x00,0x00,0x04,0x5f,0x25,0x06,0x02,0x02,0x00,0x03,0x02,0x07,0x5f,0x24,0x06,
+ 0x02,0x05,0x01,0x02,0x03,0x01,0x5f,0x37,0x30,0x8b,0xb2,0x01,0xb6,0x24,0xfe,0xe5,
+ 0x4e,0x65,0x3a,0x02,0xa2,0xb2,0x27,0x2d,0x3d,0xb4,0xb0,0xc9,0xdd,0xbf,0x10,0x6d,
+ 0x99,0x49,0x46,0xd6,0xd0,0x72,0xc1,0xf3,0x4c,0xab,0x4f,0x32,0x14,0x7c,0xb0,0x99,
+ 0xb7,0x33,0x70,0xd6,0x00,0xff,0x73,0x0c,0x5d
+};
+
+static const unsigned char termca[] = {
+ 0xfa,0x00,
+ 0x7f,0x21,0x81,0xf6,0x7f,0x4e,0x81,0xbf,0x5f,0x29,0x01,0x00,0x42,0x0e,0x45,0x53,
+ 0x44,0x56,0x43,0x41,0x48,0x53,0x4d,0x30,0x30,0x30,0x30,0x31,0x7f,0x49,0x3f,0x06,
+ 0x0a,0x04,0x00,0x7f,0x00,0x07,0x02,0x02,0x02,0x02,0x03,0x86,0x31,0x04,0x12,0xf0,
+ 0x26,0xfc,0xcd,0xa2,0xd9,0xb5,0x64,0x71,0xf7,0xf8,0x73,0xb4,0xa9,0xe4,0xaa,0x48,
+ 0xb5,0xbf,0x3a,0xd0,0x39,0x04,0xa3,0xc7,0xfd,0xd3,0xa0,0xea,0x81,0x3b,0x7d,0xe8,
+ 0xa4,0x81,0xcf,0x19,0xb8,0xd8,0x73,0x30,0x9a,0x6f,0x0d,0x69,0xe2,0x2e,0x5f,0x20,
+ 0x0e,0x45,0x53,0x54,0x45,0x52,0x4d,0x48,0x53,0x4d,0x30,0x39,0x50,0x52,0x4f,0x7f,
+ 0x4c,0x12,0x06,0x09,0x04,0x00,0x7f,0x00,0x07,0x03,0x01,0x02,0x02,0x53,0x05,0x00,
+ 0x00,0x00,0x00,0x04,0x5f,0x25,0x06,0x02,0x02,0x00,0x07,0x02,0x01,0x5f,0x24,0x06,
+ 0x02,0x03,0x00,0x07,0x02,0x01,0x65,0x2f,0x73,0x2d,0x06,0x09,0x04,0x00,0x7f,0x00,
+ 0x07,0x03,0x01,0x03,0x01,0x80,0x20,0x68,0x53,0x30,0xc7,0x9a,0x47,0xad,0xfd,0x37,
+ 0xaa,0xe8,0x53,0xf4,0xbd,0x77,0x3a,0x40,0x89,0x3a,0x79,0x7e,0x3c,0x27,0x18,0x3b,
+ 0x39,0x67,0xdf,0x8d,0x4f,0xe5,0x99,0x5f,0x37,0x30,0x50,0x86,0x9f,0x7a,0xb1,0xd0,
+ 0x05,0x89,0x5f,0x50,0x7b,0x70,0x84,0x6b,0xda,0x1d,0xfa,0x3b,0x7f,0x83,0xef,0x20,
+ 0xec,0xec,0x6f,0x90,0xe3,0x2e,0x68,0x9e,0x98,0x7c,0x4a,0xb3,0x29,0x6d,0xf8,0x75,
+ 0x2b,0x45,0x20,0xb8,0x20,0xe0,0x06,0x56,0x8f,0x2d
+};
+
+static const unsigned char termca_pk[] = {
+ 0x18,0x00,
+ 0x32,0x69,0xdd,0x64,0xda,0x06,0x3c,0xf2,0x37,0x37,0xd0,0x8b,0xbd,0xa9,0xa3,0x1c,
+ 0x54,0xc0,0x7e,0x19,0xfa,0xbe,0x47,0xc3
+};
+
+#endif
diff --git a/src/hsm/sc_hsm.c b/src/hsm/sc_hsm.c
index 734ec92..364d043 100644
--- a/src/hsm/sc_hsm.c
+++ b/src/hsm/sc_hsm.c
@@ -16,29 +16,14 @@
*/
#include "sc_hsm.h"
-#include "files.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"
#include "cvcerts.h"
#include "crypto_utils.h"
#include "kek.h"
-#include "hardware/rtc.h"
#include "eac.h"
#include "cvc.h"
#include "asn1.h"
-#include "oid.h"
-#include "mbedtls/oid.h"
-
-#define MAX_PUK 8
const uint8_t sc_hsm_aid[] = {
11,
@@ -59,7 +44,34 @@ static int sc_hsm_process_apdu();
static void init_sc_hsm();
static int sc_hsm_unload();
-static int cmd_select();
+
+extern int cmd_select();
+extern void select_file(file_t *pe);
+extern int cmd_list_keys();
+extern int cmd_read_binary();
+extern int cmd_verify();
+extern int cmd_reset_retry();
+extern int cmd_challenge();
+extern int cmd_external_authenticate();
+extern int cmd_mse();
+extern int cmd_initialize();
+extern int cmd_key_domain();
+extern int cmd_key_wrap();
+extern int cmd_keypair_gen();
+extern int cmd_update_ef();
+extern int cmd_delete_file();
+extern int cmd_change_pin();
+extern int cmd_key_gen();
+extern int cmd_signature();
+extern int cmd_key_unwrap();
+extern int cmd_decrypt_asym();
+extern int cmd_cipher_sym();
+extern int cmd_derive_asym();
+extern int cmd_extras();
+extern int cmd_general_authenticate();
+extern int cmd_session_pin();
+extern int cmd_puk_auth();
+extern int cmd_pso();
app_t *sc_hsm_select_aid(app_t *a) {
if (!memcmp(apdu.data, sc_hsm_aid+1, MIN(apdu.nc,sc_hsm_aid[0]))) {
@@ -157,7 +169,6 @@ void scan_all() {
PUK puk_store[MAX_PUK_STORE_ENTRIES];
int puk_store_entries = 0;
PUK *current_puk = NULL;
-file_t *ef_puk_aut = NULL;
uint8_t puk_status[MAX_PUK];
int add_cert_puk_store(const uint8_t *data, size_t data_len, bool copy) {
@@ -183,6 +194,16 @@ int add_cert_puk_store(const uint8_t *data, size_t data_len, bool copy) {
return CCID_OK;
}
+int puk_store_select_chr(const uint8_t *chr) {
+ for (int i = 0; i < puk_store_entries; i++) {
+ if (memcmp(puk_store[i].chr, chr, puk_store[i].chr_len) == 0) {
+ current_puk = &puk_store[i];
+ return CCID_OK;
+ }
+ }
+ return CCID_ERR_FILE_NOT_FOUND;
+}
+
void init_sc_hsm() {
scan_all();
has_session_pin = has_session_sopin = false;
@@ -216,25 +237,6 @@ int sc_hsm_unload() {
return CCID_OK;
}
-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
- }
-}
-
uint16_t get_device_options() {
file_t *ef = search_by_fid(EF_DEVOPS, NULL, SPECIFY_EF);
if (ef && ef->data && file_get_size(ef))
@@ -244,7 +246,7 @@ uint16_t get_device_options() {
extern uint32_t board_button_read(void);
-static bool wait_button() {
+bool wait_button() {
uint16_t opts = get_device_options();
uint32_t val = EV_PRESS_BUTTON;
if (opts & HSM_OPT_BOOTSEL_BUTTON) {
@@ -257,100 +259,6 @@ static bool wait_button() {
return val == EV_BUTTON_TIMEOUT;
}
-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.nc >= 2)
- fid = get_uint16_t(apdu.data, 0);
-
- //if ((fid & 0xff00) == (KEY_PREFIX << 8))
- // fid = (PRKD_PREFIX << 8) | (fid & 0xff);
-
- uint8_t pfx = fid >> 8;
- if (pfx == PRKD_PREFIX ||
- pfx == CD_PREFIX ||
- pfx == CA_CERTIFICATE_PREFIX ||
- pfx == KEY_PREFIX ||
- pfx == EE_CERTIFICATE_PREFIX ||
- pfx == DCOD_PREFIX ||
- pfx == DATA_PREFIX ||
- pfx == PROT_DATA_PREFIX) {
- 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.nc == 0) {
- pe = (file_t *)MF;
- //ac_fini();
- }
- else if (apdu.nc == 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.nc != 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.data, apdu.nc))) {
- 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.data, apdu.nc, 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.data, apdu.nc, currentDF))) {
- return SW_FILE_NOT_FOUND();
- }
- }
- }
- if ((p2 & 0xfc) == 0x00 || (p2 & 0xfc) == 0x04) {
- process_fci(pe,0);
- if (pe == file_sc_hsm) {
- res_APDU[res_APDU_size++] = 0x85;
- res_APDU[res_APDU_size++] = 5;
- uint16_t opts = get_device_options();
- res_APDU[res_APDU_size++] = opts >> 8;
- res_APDU[res_APDU_size++] = opts & 0xff;
- res_APDU[res_APDU_size++] = 0xFF;
- res_APDU[res_APDU_size++] = HSM_VERSION_MAJOR;
- res_APDU[res_APDU_size++] = HSM_VERSION_MINOR;
- res_APDU[1] = res_APDU_size-2;
- }
- }
- else
- return SW_INCORRECT_P1P2();
- select_file(pe);
- return SW_OK ();
-}
-
int parse_token_info(const file_t *f, int mode) {
char *label = "Pico-HSM";
char *manu = "Pol Henarejos";
@@ -380,110 +288,6 @@ int parse_cvca(const file_t *f, int mode) {
return termca_len+dica_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++] = PRKD_PREFIX;
- res_APDU[res_APDU_size++] = f->fid & 0xff;
- 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;
- }
- }
-
- for (int i = 0; i < dynamic_files; i++) {
- file_t *f = &dynamic_file[i];
- if ((f->fid & 0xff00) == (DCOD_PREFIX << 8)) {
- res_APDU[res_APDU_size++] = DCOD_PREFIX;
- res_APDU[res_APDU_size++] = f->fid & 0xff;
- }
- }
- return SW_OK();
-}
-
-static int cmd_read_binary()
-{
- uint16_t fid = 0x0;
- uint32_t offset = 0;
- 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.data[0] != 0x54)
- return SW_WRONG_DATA();
-
- offset = 0;
- for (int d = 0; d < apdu.data[1]; d++)
- offset |= apdu.data[2+d]<<(apdu.data[1]-1-d)*8;
- }
- }
-
- if ((fid >> 8) == KEY_PREFIX || !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.ne > maxle)
- apdu.ne = maxle;
- if (offset) {
- memmove(res_APDU, res_APDU+offset, res_APDU_size-offset);
- //res_APDU += offset;
- res_APDU_size -= offset;
- }
- }
- else {
- uint16_t data_len = file_get_size(ef);
- if (offset > data_len)
- return SW_WRONG_P1P2();
-
- uint16_t maxle = data_len-offset;
- if (apdu.ne > maxle)
- apdu.ne = maxle;
- memcpy(res_APDU, file_get_data(ef)+offset, data_len-offset);
- res_APDU_size = data_len-offset;
- }
- }
-
- return SW_OK();
-}
-
int pin_reset_retries(const file_t *pin, bool force) {
if (!pin)
return CCID_ERR_NULL_PARAM;
@@ -567,270 +371,6 @@ int check_pin(const file_t *pin, const uint8_t *data, size_t len) {
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();
-
- if (p2 == 0x81) { //UserPin
- uint16_t opts = get_device_options();
- if (opts & HSM_OPT_TRANSPORT_PIN)
- return SW_DATA_INVALID();
- if (has_session_pin && apdu.nc == 0)
- return SW_OK();
- if (*file_get_data(file_pin1) == 0 && pka_enabled() == false) //not initialized
- return SW_REFERENCE_NOT_FOUND();
- if (apdu.nc > 0) {
- return check_pin(file_pin1, apdu.data, apdu.nc);
- }
- if (file_read_uint8(file_get_data(file_retries_pin1)) == 0)
- return SW_PIN_BLOCKED();
- return set_res_sw(0x63, 0xc0 | file_read_uint8(file_get_data(file_retries_pin1)));
- }
- else if (p2 == 0x88) { //SOPin
- if (file_read_uint8(file_get_data(file_sopin)) == 0) //not initialized
- return SW_REFERENCE_NOT_FOUND();
- if (apdu.nc > 0) {
- return check_pin(file_sopin, apdu.data, apdu.nc);
- }
- if (file_read_uint8(file_get_data(file_retries_sopin)) == 0)
- return SW_PIN_BLOCKED();
- if (has_session_sopin)
- return SW_OK();
- return set_res_sw(0x63, 0xc0 | file_read_uint8(file_get_data(file_retries_sopin)));
- }
- else if (p2 == 0x85) {
- return SW_OK();
- }
- return SW_REFERENCE_NOT_FOUND();
-}
-
-static int cmd_reset_retry() {
- if (P2(apdu) != 0x81)
- return SW_REFERENCE_NOT_FOUND();
- if (!file_sopin || !file_pin1) {
- return SW_FILE_NOT_FOUND();
- }
- if (!file_sopin->data) {
- return SW_REFERENCE_NOT_FOUND();
- }
- uint16_t opts = get_device_options();
- if (!(opts & HSM_OPT_RRC))
- return SW_COMMAND_NOT_ALLOWED();
- if (P1(apdu) == 0x0 || P1(apdu) == 0x2) {
- int newpin_len = 0;
- if (P1(apdu) == 0x0) {
- if (apdu.nc <= 8)
- return SW_WRONG_LENGTH();
- uint16_t r = check_pin(file_sopin, apdu.data, 8);
- if (r != 0x9000)
- return r;
- newpin_len = apdu.nc-8;
- has_session_sopin = true;
- hash_multi(apdu.data, 8, session_sopin);
- }
- else if (P1(apdu) == 0x2) {
- if (!has_session_sopin)
- return SW_CONDITIONS_NOT_SATISFIED();
- if (apdu.nc > 16)
- return SW_WRONG_LENGTH();
- newpin_len = apdu.nc;
- }
- uint8_t dhash[33];
- dhash[0] = newpin_len;
- double_hash_pin(apdu.data+(apdu.nc-newpin_len), newpin_len, dhash+1);
- flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
- if (pin_reset_retries(file_pin1, true) != CCID_OK)
- return SW_MEMORY_FAILURE();
- uint8_t mkek[MKEK_SIZE];
- int r = load_mkek(mkek); //loads the MKEK with SO pin
- if (r != CCID_OK)
- return SW_EXEC_ERROR();
- hash_multi(apdu.data+(apdu.nc-newpin_len), newpin_len, session_pin);
- has_session_pin = true;
- r = store_mkek(mkek);
- release_mkek(mkek);
- if (r != CCID_OK)
- return SW_EXEC_ERROR();
- low_flash_available();
- return SW_OK();
- }
- else if (P1(apdu) == 0x1 || P1(apdu) == 0x3) {
- if (!(opts & HSM_OPT_RRC_RESET_ONLY))
- return SW_COMMAND_NOT_ALLOWED();
- if (P1(apdu) == 0x1) {
- if (apdu.nc != 8)
- return SW_WRONG_LENGTH();
- uint16_t r = check_pin(file_sopin, apdu.data, 8);
- if (r != 0x9000)
- return r;
- has_session_sopin = true;
- hash_multi(apdu.data, 8, session_sopin);
- }
- else if (P1(apdu) == 0x3) {
- if (!has_session_sopin)
- return SW_CONDITIONS_NOT_SATISFIED();
- if (apdu.nc != 0)
- return SW_WRONG_LENGTH();
- }
- if (pin_reset_retries(file_pin1, true) != CCID_OK)
- return SW_MEMORY_FAILURE();
- return SW_OK();
- }
- return SW_INCORRECT_P1P2();
-}
-
-static uint8_t challenge[256];
-static uint8_t challenge_len = 0;
-
-static int cmd_challenge() {
- uint8_t *rb = (uint8_t *)random_bytes_get(apdu.ne);
- if (!rb)
- return SW_WRONG_LENGTH();
- memcpy(res_APDU, rb, apdu.ne);
- challenge_len = MIN(apdu.ne, sizeof(challenge));
- memcpy(challenge, rb, challenge_len);
- res_APDU_size = apdu.ne;
- return SW_OK();
-}
-
-extern char __StackLimit;
-int heapLeft() {
- char *p = malloc(256); // try to avoid undue fragmentation
- int left = &__StackLimit - p;
- free(p);
- return left;
-}
-
-static int cmd_initialize() {
- if (apdu.nc > 0) {
- initialize_flash(true);
- scan_all();
- has_session_pin = has_session_sopin = false;
- uint16_t tag = 0x0;
- uint8_t *tag_data = NULL, *p = NULL, *kds = NULL, *dkeks = NULL;
- size_t tag_len = 0;
- while (walk_tlv(apdu.data, apdu.nc, &p, &tag, &tag_len, &tag_data)) {
- if (tag == 0x80) { //options
- file_t *tf = search_by_fid(EF_DEVOPS, NULL, SPECIFY_EF);
- flash_write_data_to_file(tf, tag_data, tag_len);
- }
- else if (tag == 0x81) { //user pin
- if (file_pin1 && file_pin1->data) {
- uint8_t dhash[33];
- dhash[0] = tag_len;
- double_hash_pin(tag_data, tag_len, dhash+1);
- flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
- hash_multi(tag_data, tag_len, session_pin);
- has_session_pin = true;
- }
- }
- else if (tag == 0x82) { //sopin pin
- if (file_sopin && file_sopin->data) {
- uint8_t dhash[33];
- dhash[0] = tag_len;
- double_hash_pin(tag_data, tag_len, dhash+1);
- flash_write_data_to_file(file_sopin, dhash, sizeof(dhash));
- hash_multi(tag_data, tag_len, session_sopin);
- has_session_sopin = true;
- }
- }
- else if (tag == 0x91) { //retries user pin
- file_t *tf = search_by_fid(0x1082, NULL, SPECIFY_EF);
- if (tf && tf->data) {
- flash_write_data_to_file(tf, tag_data, tag_len);
- }
- if (file_retries_pin1 && file_retries_pin1->data) {
- flash_write_data_to_file(file_retries_pin1, tag_data, tag_len);
- }
- }
- else if (tag == 0x92) {
- dkeks = tag_data;
- file_t *tf = file_new(EF_DKEK);
- if (!tf)
- return SW_MEMORY_FAILURE();
- flash_write_data_to_file(tf, NULL, 0);
- }
- else if (tag == 0x93) {
- file_t *ef_puk = search_by_fid(EF_PUKAUT, NULL, SPECIFY_EF);
- if (!ef_puk)
- return SW_MEMORY_FAILURE();
- uint8_t pk_status[4], puks = MIN(tag_data[0],MAX_PUK);
- memset(pk_status, 0, sizeof(pk_status));
- pk_status[0] = puks;
- pk_status[1] = puks;
- pk_status[2] = tag_data[1];
- flash_write_data_to_file(ef_puk, pk_status, sizeof(pk_status));
- for (int i = 0; i < puks; i++) {
- file_t *tf = file_new(EF_PUK+i);
- if (!tf)
- return SW_MEMORY_FAILURE();
- flash_write_data_to_file(tf, NULL, 0);
- }
- }
- else if (tag == 0x97) {
- kds = tag_data;
- /*
- for (int i = 0; i < MIN(*kds,MAX_KEY_DOMAINS); i++) {
- file_t *tf = file_new(EF_DKEK+i);
- if (!tf)
- return SW_MEMORY_FAILURE();
- flash_write_data_to_file(tf, NULL, 0);
- }
- */
- }
- }
- file_t *tf_kd = search_by_fid(EF_KEY_DOMAIN, NULL, SPECIFY_EF);
- if (!tf_kd)
- return SW_EXEC_ERROR();
- if (store_mkek(NULL) != CCID_OK)
- return SW_EXEC_ERROR();
- if (dkeks) {
- if (*dkeks > 0) {
- uint16_t d = *dkeks;
- if (flash_write_data_to_file(tf_kd, (const uint8_t *)&d, sizeof(d)) != CCID_OK)
- return SW_EXEC_ERROR();
- }
- else {
- int r = save_dkek_key(0, random_bytes_get(32));
- if (r != CCID_OK)
- return SW_EXEC_ERROR();
- uint16_t d = 0x0101;
- if (flash_write_data_to_file(tf_kd, (const uint8_t *)&d, sizeof(d)) != CCID_OK)
- return SW_EXEC_ERROR();
- }
- }
- else {
- uint16_t d = 0x0000;
- if (flash_write_data_to_file(tf_kd, (const uint8_t *)&d, sizeof(d)) != CCID_OK)
- return SW_EXEC_ERROR();
- }
- if (kds) {
- uint8_t t[MAX_KEY_DOMAINS*2], k = MIN(*kds,MAX_KEY_DOMAINS);
- memset(t, 0xff, 2*k);
- if (flash_write_data_to_file(tf_kd, t, 2*k) != CCID_OK)
- return SW_EXEC_ERROR();
- }
- /* When initialized, it has all credentials */
- isUserAuthenticated = true;
- low_flash_available();
- }
- else { //free memory bytes request
- int heap_left = heapLeft();
- res_APDU[0] = ((heap_left >> 24) & 0xff);
- res_APDU[1] = ((heap_left >> 16) & 0xff);
- res_APDU[2] = ((heap_left >> 8) & 0xff);
- res_APDU[3] = ((heap_left >> 0) & 0xff);
- res_APDU[4] = 0;
- res_APDU[5] = HSM_VERSION_MAJOR;
- res_APDU[6] = HSM_VERSION_MINOR;
- res_APDU_size = 7;
- }
- return SW_OK();
-}
-
const uint8_t *get_meta_tag(file_t *ef, uint16_t meta_tag, size_t *tag_len) {
if (ef == NULL)
return NULL;
@@ -848,14 +388,6 @@ const uint8_t *get_meta_tag(file_t *ef, uint16_t meta_tag, size_t *tag_len) {
return NULL;
}
-uint8_t get_key_domain(file_t *fkey) {
- size_t tag_len = 0;
- const uint8_t *meta_tag = get_meta_tag(fkey, 0x92, &tag_len);
- if (meta_tag)
- return *meta_tag;
- return 0xff;
-}
-
uint32_t get_key_counter(file_t *fkey) {
size_t tag_len = 0;
const uint8_t *meta_tag = get_meta_tag(fkey, 0x90, &tag_len);
@@ -921,136 +453,6 @@ int delete_file(file_t *ef) {
return CCID_OK;
}
-static int cmd_key_domain() {
- //if (dkeks == 0)
- // return SW_COMMAND_NOT_ALLOWED();
- uint8_t p1 = P1(apdu), p2 = P2(apdu);
- if ((has_session_pin == false || isUserAuthenticated == false) && apdu.nc > 0)
- return SW_CONDITIONS_NOT_SATISFIED();
- if (p2 >= MAX_KEY_DOMAINS)
- return SW_WRONG_P1P2();
- file_t *tf_kd = search_by_fid(EF_KEY_DOMAIN, NULL, SPECIFY_EF);
- if (!tf_kd)
- return SW_EXEC_ERROR();
- uint16_t tf_kd_size = file_get_size(tf_kd);
- if (tf_kd_size == 0)
- return SW_WRONG_P1P2();
- uint8_t *kdata = file_get_data(tf_kd), dkeks = kdata ? kdata[2*p2] : 0, current_dkeks = kdata ? kdata[2*p2+1] : 0;
- if (p1 == 0x0) { //dkek import
- if (apdu.nc > 0) {
- file_t *tf = file_new(EF_DKEK+p2);
- if (!tf)
- return SW_MEMORY_FAILURE();
- if (apdu.nc < 32)
- return SW_WRONG_LENGTH();
- import_dkek_share(p2, apdu.data);
- if (++current_dkeks >= dkeks) {
- if (save_dkek_key(p2, NULL) != CCID_OK)
- return SW_FILE_NOT_FOUND();
- }
- uint8_t t[MAX_KEY_DOMAINS*2];
- memcpy(t, kdata, tf_kd_size);
- t[2*p2+1] = current_dkeks;
- if (flash_write_data_to_file(tf_kd, t, tf_kd_size) != CCID_OK)
- return SW_EXEC_ERROR();
- low_flash_available();
- }
- else {
- file_t *tf = search_dynamic_file(EF_XKEK+p2);
- if (2*p2 >= tf_kd_size)
- return SW_INCORRECT_P1P2();
- if (current_dkeks == 0xff && !tf) //XKEK have always 0xff
- return SW_REFERENCE_NOT_FOUND();
- }
- }
- else if (p1 == 0x1 || p1 == 0x3 || p1 == 0x4) { //key domain setup
- if (p1 == 0x1 && apdu.nc != 1)
- return SW_WRONG_LENGTH();
- if (p1 == 0x3) { //if key domain is not empty, command is denied
- for (int i = 0; i < dynamic_files; i++) {
- if (get_key_domain(&dynamic_file[i]) == p2)
- return SW_FILE_EXISTS();
- }
- }
- uint8_t t[MAX_KEY_DOMAINS*2];
- memcpy(t, kdata, tf_kd_size);
- if (p1 == 0x1) {
- t[2*p2] = dkeks = apdu.data[0];
- t[2*p2+1] = current_dkeks = 0;
- }
- else if (p1 == 0x3) {
- t[2*p2] = dkeks = 0xff;
- t[2*p2+1] = 0xff;
- }
- else if (p1 == 0x4) {
- t[2*p2+1] = current_dkeks = 0;
- }
- if (flash_write_data_to_file(tf_kd, t, tf_kd_size) != CCID_OK)
- return SW_EXEC_ERROR();
- file_t *tf = NULL;
- if ((tf = search_dynamic_file(EF_DKEK+p2))) {
- if (delete_file(tf) != CCID_OK)
- return SW_EXEC_ERROR();
- }
- if (p1 == 0x3 && (tf = search_dynamic_file(EF_XKEK+p2))) {
- if (delete_file(tf) != CCID_OK)
- return SW_EXEC_ERROR();
- }
- low_flash_available();
- }
- else if (p1 == 0x2) { //XKEK Key Domain creation
- if (apdu.nc > 0) {
- size_t pub_len = 0;
- const uint8_t *pub = cvc_get_pub(termca+2, (termca[1] << 8 | termca[0]), &pub_len);
- if (!pub)
- return SW_EXEC_ERROR();
- size_t t86_len = 0;
- const uint8_t *t86 = cvc_get_field(pub, pub_len, &t86_len, 0x86);
- if (!t86 || t86[0] != 0x4)
- return SW_EXEC_ERROR();
- size_t t54_len = 0;
- const uint8_t *t54 = cvc_get_field(apdu.data, apdu.nc, &t54_len, 0x54);
- if (!t54)
- return SW_WRONG_DATA();
- uint8_t hash[32], *input = (uint8_t *)calloc(1, (t86_len-1)/2+1);
- input[0] = 0x54;
- memcpy(input+1, t86+1, (t86_len-1)/2);
- hash256(input, (t86_len-1)/2+1, hash);
- free(input);
- int r = puk_verify(t54, t54_len, hash, 32, apdu.data, apdu.nc);
- if (r != 0)
- return SW_CONDITIONS_NOT_SATISFIED();
- file_t *tf = file_new(EF_XKEK+p2);
- if (!tf)
- return SW_MEMORY_FAILURE();
-
- //All checks done. Get Key Domain UID
- pub = cvc_get_pub(apdu.data, apdu.nc, &pub_len);
- if (pub) {
- size_t t86_len = 0;
- const uint8_t *t86 = cvc_get_field(pub, pub_len, &t86_len, 0x86);
- if (t86) {
- flash_write_data_to_file(tf, t86+1, t86_len-1);
- low_flash_available();
- }
- }
- }
- }
- else
- return SW_INCORRECT_P1P2();
- memset(res_APDU,0,10);
- res_APDU[0] = dkeks;
- res_APDU[1] = dkeks > current_dkeks ? dkeks-current_dkeks : 0;
- dkek_kcv(p2, res_APDU+2);
- res_APDU_size = 2+8;
- file_t *tf = search_dynamic_file(EF_XKEK+p2);
- if (tf) {
- memcpy(res_APDU+10, file_get_data(tf), file_get_size(tf));
- res_APDU_size += file_get_size(tf);
- }
- return SW_OK();
-}
-
//Stores the private and public keys in flash
int store_keys(void *key_ctx, int type, uint8_t key_id) {
int r, key_size = 0;
@@ -1132,283 +534,6 @@ int find_and_store_meta_key(uint8_t key_id) {
return CCID_OK;
}
-static int cmd_keypair_gen() {
- uint8_t key_id = P1(apdu);
- if (!isUserAuthenticated)
- return SW_SECURITY_STATUS_NOT_SATISFIED();
- int ret = 0;
-
- size_t tout = 0;
- //sc_asn1_print_tags(apdu.data, apdu.nc);
- uint8_t *p = NULL;
- if (asn1_find_tag(apdu.data, apdu.nc, 0x7f49, &tout, &p) && tout > 0 && p != NULL) {
- size_t oid_len = 0;
- uint8_t *oid = NULL;
- if (asn1_find_tag(p, tout, 0x6, &oid_len, &oid) && oid_len > 0 && oid != NULL) {
- if (memcmp(oid, OID_ID_TA_RSA_V1_5_SHA_256, oid_len) == 0) { //RSA
- size_t ex_len = 3, ks_len = 2;
- uint8_t *ex = NULL, *ks = NULL;
- uint32_t exponent = 65537, key_size = 2048;
- if (asn1_find_tag(p, tout, 0x82, &ex_len, &ex) && ex_len > 0 && ex != NULL) {
- uint8_t *dt = ex;
- exponent = 0;
- for (int i = 0; i < ex_len; i++) {
- exponent = (exponent << 8) | *dt++;
- }
- }
- if (asn1_find_tag(p, tout, 0x2, &ks_len, &ks) && ks_len > 0 && ks != NULL) {
- uint8_t *dt = ks;
- key_size = 0;
- for (int i = 0; i < ks_len; i++) {
- key_size = (key_size << 8) | *dt++;
- }
- }
- printf("KEYPAIR RSA %ld (%lx)\r\n",key_size,exponent);
- 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) {
- mbedtls_rsa_free(&rsa);
- return SW_EXEC_ERROR();
- }
- if ((res_APDU_size = asn1_cvc_aut(&rsa, HSM_KEY_RSA, res_APDU, 4096, NULL, 0)) == 0) {
- return SW_EXEC_ERROR();
- }
- ret = store_keys(&rsa, HSM_KEY_RSA, key_id);
- if (ret != CCID_OK) {
- mbedtls_rsa_free(&rsa);
- return SW_EXEC_ERROR();
- }
- mbedtls_rsa_free(&rsa);
- }
- else if (memcmp(oid, OID_IT_TA_ECDSA_SHA_256,MIN(oid_len,10)) == 0) { //ECC
- size_t prime_len;
- uint8_t *prime = NULL;
- if (asn1_find_tag(p, tout, 0x81, &prime_len, &prime) != true)
- return SW_WRONG_DATA();
- mbedtls_ecp_group_id ec_id = ec_get_curve_from_prime(prime, prime_len);
- printf("KEYPAIR ECC %d\r\n",ec_id);
- if (ec_id == MBEDTLS_ECP_DP_NONE) {
- 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) {
- mbedtls_ecdsa_free(&ecdsa);
- return SW_EXEC_ERROR();
- }
- size_t l91 = 0, ext_len = 0;
- uint8_t *p91 = NULL, *ext = NULL;
- if (asn1_find_tag(apdu.data, apdu.nc, 0x91, &l91, &p91) && p91 != NULL && l91 > 0) {
- for (int n = 0; n < l91; n++) {
- if (p91[n] == ALGO_EC_DH_XKEK) {
- size_t l92 = 0;
- uint8_t *p92 = NULL;
- if (!asn1_find_tag(apdu.data, apdu.nc, 0x92, &l92, &p92) || p92 == NULL || l92 == 0)
- return SW_WRONG_DATA();
- if (p92[0] > MAX_KEY_DOMAINS)
- return SW_WRONG_DATA();
- file_t *tf_xkek = search_dynamic_file(EF_XKEK+p92[0]);
- if (!tf_xkek)
- return SW_WRONG_DATA();
- ext_len = 2+2+strlen(OID_ID_KEY_DOMAIN_UID)+2+file_get_size(tf_xkek);
- ext = (uint8_t *)calloc(1, ext_len);
- uint8_t *pe = ext;
- *pe++ = 0x73;
- *pe++ = ext_len-2;
- *pe++ = 0x6;
- *pe++ = strlen(OID_ID_KEY_DOMAIN_UID);
- memcpy(pe, OID_ID_KEY_DOMAIN_UID, strlen(OID_ID_KEY_DOMAIN_UID));
- pe += strlen(OID_ID_KEY_DOMAIN_UID);
- *pe++ = 0x80;
- *pe++ = file_get_size(tf_xkek);
- memcpy(pe, file_get_data(tf_xkek), file_get_size(tf_xkek));
- }
- }
- }
- if ((res_APDU_size = asn1_cvc_aut(&ecdsa, HSM_KEY_EC, res_APDU, 4096, ext, ext_len)) == 0) {
- return SW_EXEC_ERROR();
- }
- ret = store_keys(&ecdsa, HSM_KEY_EC, key_id);
- if (ret != CCID_OK) {
- mbedtls_ecdsa_free(&ecdsa);
- return SW_EXEC_ERROR();
- }
- mbedtls_ecdsa_free(&ecdsa);
- }
-
- }
- }
- else
- return SW_WRONG_DATA();
- if (find_and_store_meta_key(key_id) != CCID_OK)
- return SW_EXEC_ERROR();
- 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();
- if (apdu.ne == 0)
- apdu.ne = res_APDU_size;
- low_flash_available();
- return SW_OK();
-}
-
-static int cmd_update_ef() {
- uint8_t p1 = P1(apdu), p2 = P2(apdu);
- uint16_t fid = (p1 << 8) | p2;
- uint8_t *data = NULL;
- 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 && p1 != DATA_PREFIX && p1 != DCOD_PREFIX && p1 != PROT_DATA_PREFIX)
- return SW_INCORRECT_P1P2();
-
- if (ef && !authenticate_action(ef, ACL_OP_UPDATE_ERASE))
- return SW_SECURITY_STATUS_NOT_SATISFIED();
-
- uint16_t tag = 0x0;
- uint8_t *tag_data = NULL, *p = NULL;
- size_t tag_len = 0;
- while (walk_tlv(apdu.data, apdu.nc, &p, &tag, &tag_len, &tag_data)) {
- if (tag == 0x54) { //ofset tag
- for (int i = 1; i <= tag_len; i++)
- offset |= (*tag_data++ << (8*(tag_len-i)));
- }
- else if (tag == 0x53) { //data
- data_len = tag_len;
- data = tag_data;
- }
- }
- 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 != CCID_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_get_data(ef), offset);
- memcpy(data_merge+offset, data, data_len);
- int r = flash_write_data_to_file(ef, data_merge, offset+data_len);
- free(data_merge);
- if (r != CCID_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.nc == 0) {
- ef = currentEF;
- if (!(ef = search_dynamic_file(ef->fid)))
- return SW_FILE_NOT_FOUND();
- }
- else {
- uint16_t fid = (apdu.data[0] << 8) | 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 (delete_file(ef) != CCID_OK)
- return SW_EXEC_ERROR();
- 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_get_data(file_pin1));
- int r = check_pin(file_pin1, apdu.data, pin_len);
- if (r != 0x9000)
- return r;
- uint8_t mkek[MKEK_SIZE];
- r = load_mkek(mkek); //loads the MKEK with old pin
- if (r != CCID_OK)
- return SW_EXEC_ERROR();
- //encrypt MKEK with new pin
- hash_multi(apdu.data+pin_len, apdu.nc-pin_len, session_pin);
- has_session_pin = true;
- r = store_mkek(mkek);
- release_mkek(mkek);
- if (r != CCID_OK)
- return SW_EXEC_ERROR();
- uint8_t dhash[33];
- dhash[0] = apdu.nc-pin_len;
- double_hash_pin(apdu.data+pin_len, apdu.nc-pin_len, dhash+1);
- flash_write_data_to_file(file_pin1, dhash, sizeof(dhash));
- low_flash_available();
- return SW_OK();
- }
- }
- return SW_WRONG_P1P2();
-}
-
-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);
- int aes_type = 0x0;
- if (key_size == 16)
- aes_type = HSM_KEY_AES_128;
- else if (key_size == 24)
- aes_type = HSM_KEY_AES_192;
- else if (key_size == 32)
- aes_type = HSM_KEY_AES_256;
- r = store_keys(aes_key, aes_type, key_id);
- if (r != CCID_OK)
- return SW_MEMORY_FAILURE();
- if (find_and_store_meta_key(key_id) != CCID_OK)
- return SW_EXEC_ERROR();
- low_flash_available();
- return SW_OK();
-}
-
int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey) {
if (wait_button() == true) //timeout
return CCID_VERIFICATION_FAILED;
@@ -1473,1094 +598,6 @@ int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey) {
return CCID_OK;
}
-//-----
-/* From OpenSC */
-static const uint8_t hdr_md5[] = {
- 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7,
- 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10
-};
-static const uint8_t hdr_sha1[] = {
- 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
- 0x05, 0x00, 0x04, 0x14
-};
-static const uint8_t hdr_sha256[] = {
- 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
- 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
-};
-static const uint8_t hdr_sha384[] = {
- 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
- 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30
-};
-static const uint8_t hdr_sha512[] = {
- 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
- 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40
-};
-static const uint8_t hdr_sha224[] = {
- 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
- 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c
-};
-static const uint8_t hdr_ripemd160[] = {
- 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01,
- 0x05, 0x00, 0x04, 0x14
-};
-static const struct digest_info_prefix {
- mbedtls_md_type_t algorithm;
- const uint8_t * hdr;
- size_t hdr_len;
- size_t hash_len;
-} digest_info_prefix[] = {
- { MBEDTLS_MD_MD5, hdr_md5, sizeof(hdr_md5), 16 },
- { MBEDTLS_MD_SHA1, hdr_sha1, sizeof(hdr_sha1), 20 },
- { MBEDTLS_MD_SHA256, hdr_sha256, sizeof(hdr_sha256), 32 },
- { MBEDTLS_MD_SHA384, hdr_sha384, sizeof(hdr_sha384), 48 },
- { MBEDTLS_MD_SHA512, hdr_sha512, sizeof(hdr_sha512), 64 },
- { MBEDTLS_MD_SHA224, hdr_sha224, sizeof(hdr_sha224), 28 },
- { MBEDTLS_MD_RIPEMD160,hdr_ripemd160, sizeof(hdr_ripemd160), 20 },
- { 0, NULL, 0, 0 }
-};
-int pkcs1_strip_digest_info_prefix(mbedtls_md_type_t *algorithm, const uint8_t *in_dat, size_t in_len, uint8_t *out_dat, size_t *out_len)
-{
- for (int i = 0; digest_info_prefix[i].algorithm != 0; i++) {
- size_t hdr_len = digest_info_prefix[i].hdr_len, hash_len = digest_info_prefix[i].hash_len;
- const uint8_t *hdr = digest_info_prefix[i].hdr;
- if (in_len == (hdr_len + hash_len) && !memcmp(in_dat, hdr, hdr_len)) {
- if (algorithm)
- *algorithm = digest_info_prefix[i].algorithm;
- if (out_dat == NULL)
- return CCID_OK;
- if (*out_len < hash_len)
- return CCID_WRONG_DATA;
- memmove(out_dat, in_dat + hdr_len, hash_len);
- *out_len = hash_len;
- return CCID_OK;
- }
- }
- return CCID_EXEC_ERROR;
-}
-//-------
-
-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 || file_get_size(fkey) == 0)
- return SW_FILE_NOT_FOUND();
- if (get_key_counter(fkey) == 0)
- return SW_FILE_FULL();
- if (key_has_purpose(fkey, p2) == false)
- return SW_CONDITIONS_NOT_SATISFIED();
- int key_size = file_get_size(fkey);
- 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.data, apdu.nc, apdu.data);
- apdu.nc = mbedtls_md_get_size(mbedtls_md_info_from_type(md));
- }
- if (p2 >= ALGO_RSA_RAW && p2 <= ALGO_RSA_PSS_SHA512) {
- mbedtls_rsa_context ctx;
- mbedtls_rsa_init(&ctx);
-
- int r;
- r = load_private_key_rsa(&ctx, fkey);
- if (r != CCID_OK) {
- mbedtls_rsa_free(&ctx);
- if (r == CCID_VERIFICATION_FAILED)
- return SW_SECURE_MESSAGE_EXEC_ERROR();
- return SW_EXEC_ERROR();
- }
- uint8_t *hash = apdu.data;
- size_t hash_len = apdu.nc;
- if (p2 == ALGO_RSA_PKCS1) { //DigestInfo attached
- size_t nc = apdu.nc;
- if (pkcs1_strip_digest_info_prefix(&md, apdu.data, apdu.nc, apdu.data, &nc) != CCID_OK) //gets the MD algo id and strips it off
- return SW_EXEC_ERROR();
- apdu.nc = nc;
- }
- else {
- //sc_asn1_print_tags(apdu.data, apdu.nc);
- size_t tout = 0, oid_len = 0;
- uint8_t *p = NULL, *oid = NULL;
- if (asn1_find_tag(apdu.data, apdu.nc, 0x30, &tout, &p) && tout > 0 && p != NULL) {
- size_t tout30 = 0;
- uint8_t *c30 = NULL;
- if (asn1_find_tag(p, tout, 0x30, &tout30, &c30) && tout30 > 0 && c30 != NULL) {
- asn1_find_tag(c30, tout30, 0x6, &oid_len, &oid);
- }
- asn1_find_tag(p, tout, 0x4, &hash_len, &hash);
- }
- if (oid && oid_len > 0) {
- if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA1, oid_len) == 0)
- md = MBEDTLS_MD_SHA1;
- else if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA224, oid_len) == 0)
- md = MBEDTLS_MD_SHA224;
- else if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA256, oid_len) == 0)
- md = MBEDTLS_MD_SHA256;
- else if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA384, oid_len) == 0)
- md = MBEDTLS_MD_SHA384;
- else if (memcmp(oid, MBEDTLS_OID_DIGEST_ALG_SHA512, oid_len) == 0)
- md = MBEDTLS_MD_SHA512;
- }
- if (p2 >= ALGO_RSA_PSS && p2 <= ALGO_RSA_PSS_SHA512) {
- if (p2 == ALGO_RSA_PSS && !oid) {
- if (apdu.nc == 20) //default is sha1
- md = MBEDTLS_MD_SHA1;
- else if (apdu.nc == 28)
- md = MBEDTLS_MD_SHA224;
- else if (apdu.nc == 32)
- md = MBEDTLS_MD_SHA256;
- else if (apdu.nc == 48)
- md = MBEDTLS_MD_SHA384;
- else if (apdu.nc == 64)
- md = MBEDTLS_MD_SHA512;
- }
- mbedtls_rsa_set_padding(&ctx, MBEDTLS_RSA_PKCS_V21, md);
- }
- }
- if (md == MBEDTLS_MD_NONE) {
- if (apdu.nc < key_size) //needs padding
- memset(apdu.data+apdu.nc, 0, key_size-apdu.nc);
- r = mbedtls_rsa_private(&ctx, random_gen, NULL, 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.ne = key_size;
- mbedtls_rsa_free(&ctx);
- }
- else if (p2 >= ALGO_EC_RAW && p2 <= ALGO_EC_SHA512) {
- mbedtls_ecdsa_context ctx;
- mbedtls_ecdsa_init(&ctx);
- md = MBEDTLS_MD_SHA256;
- if (p2 == ALGO_EC_RAW) {
- if (apdu.nc == 32)
- md = MBEDTLS_MD_SHA256;
- else if (apdu.nc == 20)
- md = MBEDTLS_MD_SHA1;
- else if (apdu.nc == 28)
- md = MBEDTLS_MD_SHA224;
- else if (apdu.nc == 48)
- md = MBEDTLS_MD_SHA384;
- else if (apdu.nc == 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;
- else if (p2 == ALGO_EC_SHA384)
- md = MBEDTLS_MD_SHA384;
- else if (p2 == ALGO_EC_SHA512)
- md = MBEDTLS_MD_SHA512;
- int r;
- r = load_private_key_ecdsa(&ctx, fkey);
- if (r != CCID_OK) {
- mbedtls_ecdsa_free(&ctx);
- if (r == CCID_VERIFICATION_FAILED)
- return SW_SECURE_MESSAGE_EXEC_ERROR();
- return SW_EXEC_ERROR();
- }
- size_t olen = 0;
- uint8_t buf[MBEDTLS_ECDSA_MAX_LEN];
- if (mbedtls_ecdsa_write_signature(&ctx, md, apdu.data, apdu.nc, 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();
- decrement_key_counter(fkey);
- return SW_OK();
-}
-
-static int cmd_key_wrap() {
- int key_id = P1(apdu), r = 0;
- 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);
- uint8_t kdom = get_key_domain(ef);
- if (!ef)
- return SW_FILE_NOT_FOUND();
- if (key_has_purpose(ef, ALGO_WRAP) == false)
- return SW_CONDITIONS_NOT_SATISFIED();
- file_t *prkd = search_dynamic_file((PRKD_PREFIX << 8) | key_id);
- if (!prkd)
- return SW_FILE_NOT_FOUND();
- const uint8_t *dprkd = file_get_data(prkd);
- size_t wrap_len = MAX_DKEK_ENCODE_KEY_BUFFER;
- size_t tag_len = 0;
- const uint8_t *meta_tag = get_meta_tag(ef, 0x91, &tag_len);
- if (*dprkd == P15_KEYTYPE_RSA) {
- mbedtls_rsa_context ctx;
- mbedtls_rsa_init(&ctx);
- r = load_private_key_rsa(&ctx, ef);
- if (r != CCID_OK) {
- mbedtls_rsa_free(&ctx);
- if (r == CCID_VERIFICATION_FAILED)
- return SW_SECURE_MESSAGE_EXEC_ERROR();
- return SW_EXEC_ERROR();
- }
- r = dkek_encode_key(kdom, &ctx, HSM_KEY_RSA, res_APDU, &wrap_len, meta_tag, tag_len);
- mbedtls_rsa_free(&ctx);
- }
- else if (*dprkd == P15_KEYTYPE_ECC) {
- mbedtls_ecdsa_context ctx;
- mbedtls_ecdsa_init(&ctx);
- r = load_private_key_ecdsa(&ctx, ef);
- if (r != CCID_OK) {
- mbedtls_ecdsa_free(&ctx);
- if (r == CCID_VERIFICATION_FAILED)
- return SW_SECURE_MESSAGE_EXEC_ERROR();
- return SW_EXEC_ERROR();
- }
- r = dkek_encode_key(kdom, &ctx, HSM_KEY_EC, res_APDU, &wrap_len, meta_tag, tag_len);
- mbedtls_ecdsa_free(&ctx);
- }
- else if (*dprkd == P15_KEYTYPE_AES) {
- uint8_t kdata[32]; //maximum AES key size
- if (wait_button() == true) //timeout
- return SW_SECURE_MESSAGE_EXEC_ERROR();
-
- int key_size = file_get_size(ef), aes_type = HSM_KEY_AES;
- memcpy(kdata, file_get_data(ef), key_size);
- if (mkek_decrypt(kdata, key_size) != 0) {
- return SW_EXEC_ERROR();
- }
- if (key_size == 32)
- aes_type = HSM_KEY_AES_256;
- else if (key_size == 24)
- aes_type = HSM_KEY_AES_192;
- else if (key_size == 16)
- aes_type = HSM_KEY_AES_128;
- r = dkek_encode_key(kdom, kdata, aes_type, res_APDU, &wrap_len, meta_tag, tag_len);
- mbedtls_platform_zeroize(kdata, sizeof(kdata));
- }
- if (r != CCID_OK)
- return SW_EXEC_ERROR();
- res_APDU_size = wrap_len;
- return SW_OK();
-}
-
-static int cmd_key_unwrap() {
- int key_id = P1(apdu), r = 0;
- if (P2(apdu) != 0x93)
- return SW_WRONG_P1P2();
- if (!isUserAuthenticated)
- return SW_SECURITY_STATUS_NOT_SATISFIED();
- int key_type = dkek_type_key(apdu.data);
- uint8_t kdom = -1, *allowed = NULL;
- size_t allowed_len = 0;
- if (key_type == 0x0)
- return SW_DATA_INVALID();
- if (key_type == HSM_KEY_RSA) {
- mbedtls_rsa_context ctx;
- mbedtls_rsa_init(&ctx);
- do {
- r = dkek_decode_key(++kdom, &ctx, apdu.data, apdu.nc, NULL, &allowed, &allowed_len);
- } while((r == CCID_ERR_FILE_NOT_FOUND || r == CCID_WRONG_DKEK) && kdom < MAX_KEY_DOMAINS);
- if (r != CCID_OK) {
- mbedtls_rsa_free(&ctx);
- return SW_EXEC_ERROR();
- }
- r = store_keys(&ctx, HSM_KEY_RSA, key_id);
- mbedtls_rsa_free(&ctx);
- if (r != CCID_OK) {
- return SW_EXEC_ERROR();
- }
- }
- else if (key_type == HSM_KEY_EC) {
- mbedtls_ecdsa_context ctx;
- mbedtls_ecdsa_init(&ctx);
- do {
- r = dkek_decode_key(++kdom, &ctx, apdu.data, apdu.nc, NULL, &allowed, &allowed_len);
- } while((r == CCID_ERR_FILE_NOT_FOUND || r == CCID_WRONG_DKEK) && kdom < MAX_KEY_DOMAINS);
- if (r != CCID_OK) {
- mbedtls_ecdsa_free(&ctx);
- return SW_EXEC_ERROR();
- }
- r = store_keys(&ctx, HSM_KEY_EC, key_id);
- mbedtls_ecdsa_free(&ctx);
- if (r != CCID_OK) {
- return SW_EXEC_ERROR();
- }
- }
- else if (key_type == HSM_KEY_AES) {
- uint8_t aes_key[32];
- int key_size = 0, aes_type = 0;
- do {
- r = dkek_decode_key(++kdom, aes_key, apdu.data, apdu.nc, &key_size, &allowed, &allowed_len);
- } while((r == CCID_ERR_FILE_NOT_FOUND || r == CCID_WRONG_DKEK) && kdom < MAX_KEY_DOMAINS);
- if (r != CCID_OK) {
- return SW_EXEC_ERROR();
- }
- if (key_size == 32)
- aes_type = HSM_KEY_AES_256;
- else if (key_size == 24)
- aes_type = HSM_KEY_AES_192;
- else if (key_size == 16)
- aes_type = HSM_KEY_AES_128;
- else
- return SW_EXEC_ERROR();
- r = store_keys(aes_key, aes_type, key_id);
- if (r != CCID_OK) {
- return SW_EXEC_ERROR();
- }
- }
- if ((allowed != NULL && allowed_len > 0) || kdom >= 0) {
- size_t meta_len = (allowed_len > 0 ? 2+allowed_len : 0) + (kdom >= 0 ? 3 : 0);
- uint8_t *meta = (uint8_t *)calloc(1,meta_len), *m = meta;
- if (allowed_len > 0) {
- *m++ = 0x91;
- *m++ = allowed_len;
- memcpy(m, allowed, allowed_len); m += allowed_len;
- }
- if (kdom >= 0) {
- *m++ = 0x92;
- *m++ = 1;
- *m++ = kdom;
- }
- r = meta_add((KEY_PREFIX << 8) | key_id, meta, meta_len);
- free(meta);
- if (r != CCID_OK)
- return r;
- }
- return SW_OK();
-}
-
-static int cmd_decrypt_asym() {
- int key_id = P1(apdu);
- uint8_t p2 = 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 (get_key_counter(ef) == 0)
- return SW_FILE_FULL();
- if (key_has_purpose(ef, p2) == false)
- return SW_CONDITIONS_NOT_SATISFIED();
- if (p2 >= ALGO_RSA_DECRYPT && p2 <= ALGO_RSA_DECRYPT_OEP) {
- mbedtls_rsa_context ctx;
- mbedtls_rsa_init(&ctx);
- if (p2 == ALGO_RSA_DECRYPT_OEP)
- mbedtls_rsa_set_padding(&ctx, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_NONE);
- int r = load_private_key_rsa(&ctx, ef);
- if (r != CCID_OK) {
- mbedtls_rsa_free(&ctx);
- if (r == CCID_VERIFICATION_FAILED)
- return SW_SECURE_MESSAGE_EXEC_ERROR();
- return SW_EXEC_ERROR();
- }
- int key_size = file_get_size(ef);
- if (apdu.nc < key_size) //needs padding
- memset(apdu.data+apdu.nc, 0, key_size-apdu.nc);
- if (p2 == ALGO_RSA_DECRYPT_PKCS1 || p2 == ALGO_RSA_DECRYPT_OEP) {
- size_t olen = apdu.nc;
- r = mbedtls_rsa_pkcs1_decrypt(&ctx, random_gen, NULL, &olen, apdu.data, res_APDU, 512);
- if (r == 0)
- res_APDU_size = olen;
- }
- else {
- r = mbedtls_rsa_private(&ctx, random_gen, NULL, apdu.data, res_APDU);
- if (r == 0)
- res_APDU_size = key_size;
- }
- if (r != 0) {
- mbedtls_rsa_free(&ctx);
- return SW_EXEC_ERROR();
- }
- mbedtls_rsa_free(&ctx);
- }
- else if (p2 == ALGO_EC_DH || p2 == ALGO_EC_DH_XKEK) {
- mbedtls_ecdh_context ctx;
- if (wait_button() == true) //timeout
- return SW_SECURE_MESSAGE_EXEC_ERROR();
- int key_size = file_get_size(ef);
- uint8_t *kdata = (uint8_t *)calloc(1,key_size);
- memcpy(kdata, file_get_data(ef), key_size);
- if (mkek_decrypt(kdata, key_size) != 0) {
- mbedtls_platform_zeroize(kdata, key_size);
- free(kdata);
- return SW_EXEC_ERROR();
- }
- mbedtls_ecdh_init(&ctx);
- mbedtls_ecp_group_id gid = kdata[0];
- int r = 0;
- r = mbedtls_ecdh_setup(&ctx, gid);
- if (r != 0) {
- mbedtls_platform_zeroize(kdata, key_size);
- 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);
- mbedtls_platform_zeroize(kdata, key_size);
- free(kdata);
- if (r != 0) {
- mbedtls_ecdh_free(&ctx);
- return SW_DATA_INVALID();
- }
- r = -1;
- if (p2 == ALGO_EC_DH)
- r = mbedtls_ecdh_read_public(&ctx, apdu.data-1, apdu.nc+1);
- else if (p2 == ALGO_EC_DH_XKEK) {
- size_t pub_len = 0;
- const uint8_t *pub = cvc_get_pub(apdu.data, apdu.nc, &pub_len);
- if (pub) {
- size_t t86_len = 0;
- const uint8_t *t86 = cvc_get_field(pub, pub_len, &t86_len, 0x86);
- if (t86) {
- r = mbedtls_ecdh_read_public(&ctx, t86-1, t86_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();
- }
- if (p2 == ALGO_EC_DH)
- res_APDU_size = olen+1;
- else {
- res_APDU_size = 0;
- size_t ext_len = 0;
- const uint8_t *ext = NULL;
- if ((ext = cvc_get_ext(apdu.data, apdu.nc, &ext_len)) == NULL)
- return SW_WRONG_DATA();
- uint8_t *p = NULL, *tag_data = NULL, *kdom_uid = NULL;
- uint16_t tag = 0;
- size_t tag_len = 0, kdom_uid_len = 0;
- while (walk_tlv(ext, ext_len, &p, &tag, &tag_len, &tag_data)) {
- if (tag == 0x73) {
- size_t oid_len = 0;
- uint8_t *oid_data = NULL;
- if (asn1_find_tag(tag_data, tag_len, 0x6, &oid_len, &oid_data) == true && oid_len == strlen(OID_ID_KEY_DOMAIN_UID) && memcmp(oid_data, OID_ID_KEY_DOMAIN_UID, strlen(OID_ID_KEY_DOMAIN_UID)) == 0) {
- if (asn1_find_tag(tag_data, tag_len, 0x80, &kdom_uid_len, &kdom_uid) == false)
- return SW_WRONG_DATA();
- break;
- }
- }
- }
- if (kdom_uid_len == 0 || kdom_uid == NULL)
- return SW_WRONG_DATA();
- for (int n = 0; n < MAX_KEY_DOMAINS; n++) {
- file_t *tf = search_dynamic_file(EF_XKEK+n);
- if (tf) {
- if (file_get_size(tf) == kdom_uid_len && memcmp(file_get_data(tf), kdom_uid, kdom_uid_len) == 0) {
- file_new(EF_DKEK+n);
- if (store_dkek_key(n, res_APDU+1) != CCID_OK)
- return SW_EXEC_ERROR();
- return SW_OK();
- }
- }
- }
- return SW_REFERENCE_NOT_FOUND();
- }
- mbedtls_ecdh_free(&ctx);
- }
- else
- return SW_WRONG_P1P2();
- decrement_key_counter(ef);
- 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 (key_has_purpose(ef, algo) == false)
- return SW_CONDITIONS_NOT_SATISFIED();
- if ((apdu.nc % 16) != 0) {
- return SW_WRONG_LENGTH();
- }
- if (wait_button() == true) //timeout
- return SW_SECURE_MESSAGE_EXEC_ERROR();
- int key_size = file_get_size(ef);
- uint8_t kdata[32]; //maximum AES key size
- memcpy(kdata, file_get_data(ef), key_size);
- if (mkek_decrypt(kdata, key_size) != 0) {
- return SW_EXEC_ERROR();
- }
- 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_platform_zeroize(kdata, sizeof(kdata));
- mbedtls_aes_free(&aes);
- return SW_EXEC_ERROR();
- }
- r = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, apdu.nc, tmp_iv, apdu.data, res_APDU);
- mbedtls_platform_zeroize(kdata, sizeof(kdata));
- 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_platform_zeroize(kdata, sizeof(kdata));
- mbedtls_aes_free(&aes);
- return SW_EXEC_ERROR();
- }
- r = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, apdu.nc, tmp_iv, apdu.data, res_APDU);
- mbedtls_platform_zeroize(kdata, sizeof(kdata));
- if (r != 0) {
- mbedtls_aes_free(&aes);
- return SW_EXEC_ERROR();
- }
- }
- res_APDU_size = apdu.nc;
- 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 {
- mbedtls_platform_zeroize(kdata, sizeof(kdata));
- return SW_WRONG_DATA();
- }
- int r = mbedtls_cipher_cmac(cipher_info, kdata, key_size*8, apdu.data, apdu.nc, res_APDU);
- mbedtls_platform_zeroize(kdata, sizeof(kdata));
- if (r != 0)
- return SW_EXEC_ERROR();
- res_APDU_size = 16;
- }
- else if (algo == ALGO_AES_DERIVE) {
- int r = mbedtls_hkdf(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), NULL, 0, file_get_data(ef), key_size, apdu.data, apdu.nc, res_APDU, apdu.nc);
- mbedtls_platform_zeroize(kdata, sizeof(kdata));
- if (r != 0)
- return SW_EXEC_ERROR();
- res_APDU_size = apdu.nc;
- }
- else {
- mbedtls_platform_zeroize(kdata, sizeof(kdata));
- 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 || file_get_size(fkey) == 0)
- return SW_FILE_NOT_FOUND();
- if (key_has_purpose(fkey, ALGO_EC_DERIVE) == false)
- return SW_CONDITIONS_NOT_SATISFIED();
- if (apdu.nc == 0)
- return SW_WRONG_LENGTH();
- if (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 != CCID_OK) {
- mbedtls_ecdsa_free(&ctx);
- if (r == CCID_VERIFICATION_FAILED)
- return SW_SECURE_MESSAGE_EXEC_ERROR();
- return SW_EXEC_ERROR();
- }
- mbedtls_mpi a, nd;
- mbedtls_mpi_init(&a);
- mbedtls_mpi_init(&nd);
- r = mbedtls_mpi_read_binary(&a, apdu.data+1, apdu.nc-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();
- }
- r = store_keys(&ctx, HSM_KEY_EC, dest_id);
- if (r != CCID_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();
-}
-
-static int cmd_extras() {
- if (P2(apdu) != 0x0)
- return SW_INCORRECT_P1P2();
- if (P1(apdu) == 0xA) { //datetime operations
- if (apdu.nc == 0) {
- datetime_t dt;
- if (!rtc_get_datetime(&dt))
- return SW_EXEC_ERROR();
- res_APDU[res_APDU_size++] = dt.year >> 8;
- res_APDU[res_APDU_size++] = dt.year & 0xff;
- res_APDU[res_APDU_size++] = dt.month;
- res_APDU[res_APDU_size++] = dt.day;
- res_APDU[res_APDU_size++] = dt.dotw;
- res_APDU[res_APDU_size++] = dt.hour;
- res_APDU[res_APDU_size++] = dt.min;
- res_APDU[res_APDU_size++] = dt.sec;
- }
- else {
- if (apdu.nc != 8)
- return SW_WRONG_LENGTH();
- datetime_t dt;
- dt.year = (apdu.data[0] << 8) | (apdu.data[1]);
- dt.month = apdu.data[2];
- dt.day = apdu.data[3];
- dt.dotw = apdu.data[4];
- dt.hour = apdu.data[5];
- dt.min = apdu.data[6];
- dt.sec = apdu.data[7];
- if (!rtc_set_datetime(&dt))
- return SW_WRONG_DATA();
- }
- }
- else if (P1(apdu) == 0x6) { //dynamic options
- if (apdu.nc > sizeof(uint8_t))
- return SW_WRONG_LENGTH();
- uint16_t opts = get_device_options();
- if (apdu.nc == 0) {
- res_APDU[res_APDU_size++] = opts >> 8;
- res_APDU[res_APDU_size++] = opts & 0xff;
- }
- else {
- uint8_t newopts[] = { apdu.data[0], (opts & 0xff) };
- file_t *tf = search_by_fid(EF_DEVOPS, NULL, SPECIFY_EF);
- flash_write_data_to_file(tf, newopts, sizeof(newopts));
- low_flash_available();
- }
- }
- else
- return SW_INCORRECT_P1P2();
- return SW_OK();
-}
-
-static int cmd_mse() {
- int p1 = P1(apdu);
- int p2 = P2(apdu);
- if (p2 != 0xA4 && p2 != 0xA6 && p2 != 0xAA && p2 != 0xB4 && p2 != 0xB6 && p2 != 0xB8)
- return SW_INCORRECT_P1P2();
- if (p1 & 0x1) { //SET
- uint16_t tag = 0x0;
- uint8_t *tag_data = NULL, *p = NULL;
- size_t tag_len = 0;
- while (walk_tlv(apdu.data, apdu.nc, &p, &tag, &tag_len, &tag_data)) {
- if (tag == 0x80) {
- if (p2 == 0xA4) {
- if (tag_len == 10 && memcmp(tag_data, OID_ID_CA_ECDH_AES_CBC_CMAC_128, tag_len) == 0)
- sm_set_protocol(MSE_AES);
- else if (tag_len == 10 && memcmp(tag_data, OID_ID_CA_ECDH_3DES_CBC_CBC, tag_len) == 0)
- sm_set_protocol(MSE_3DES);
- }
- }
- else if (tag == 0x83) {
- if (tag_len == 1) {
-
- }
- else {
- if (p2 == 0xB6) {
- for (int i = 0; i < puk_store_entries; i++) {
- if (memcmp(puk_store[i].chr, tag_data, puk_store[i].chr_len) == 0) {
- current_puk = &puk_store[i];
- return SW_OK();
- }
- }
- }
- else if (p2 == 0xA4) { /* Aut */
- for (int i = 0; i < MAX_PUK; i++) {
- file_t *ef = search_dynamic_file(EF_PUK+i);
- if (!ef)
- break;
- if (ef->data == NULL || file_get_size(ef) == 0)
- break;
- size_t chr_len = 0;
- const uint8_t *chr = cvc_get_chr(file_get_data(ef), file_get_size(ef), &chr_len);
- if (memcmp(chr, tag_data, chr_len) == 0) {
- ef_puk_aut = ef;
- return SW_OK();
- }
- }
- }
- return SW_REFERENCE_NOT_FOUND();
- }
- }
- }
- }
- else
- return SW_INCORRECT_P1P2();
- return SW_OK();
-}
-
-int cmd_general_authenticate() {
- if (P1(apdu) == 0x0 && P2(apdu) == 0x0) {
- if (apdu.data[0] == 0x7C) {
- int r = 0;
- size_t pubkey_len = 0;
- const uint8_t *pubkey = NULL;
- uint16_t tag = 0x0;
- uint8_t *tag_data = NULL, *p = NULL;
- size_t tag_len = 0;
- while (walk_tlv(apdu.data+2, apdu.nc-2, &p, &tag, &tag_len, &tag_data)) {
- if (tag == 0x80) {
- pubkey = tag_data-1; //mbedtls ecdh starts reading one pos before
- pubkey_len = tag_len+1;
- }
- }
- mbedtls_ecdh_context ctx;
- int key_size = file_read_uint16(termca_pk);
- mbedtls_ecdh_init(&ctx);
- mbedtls_ecp_group_id gid = MBEDTLS_ECP_DP_SECP192R1;
- r = mbedtls_ecdh_setup(&ctx, gid);
- if (r != 0) {
- mbedtls_ecdh_free(&ctx);
- return SW_DATA_INVALID();
- }
- r = mbedtls_mpi_read_binary(&ctx.ctx.mbed_ecdh.d, termca_pk+2, key_size);
- if (r != 0) {
- mbedtls_ecdh_free(&ctx);
- return SW_DATA_INVALID();
- }
- r = mbedtls_ecdh_read_public(&ctx, pubkey, pubkey_len);
- if (r != 0) {
- mbedtls_ecdh_free(&ctx);
- return SW_DATA_INVALID();
- }
- size_t olen = 0;
- uint8_t derived[MBEDTLS_ECP_MAX_BYTES];
- r = mbedtls_ecdh_calc_secret(&ctx, &olen, derived, MBEDTLS_ECP_MAX_BYTES, random_gen, NULL);
- mbedtls_ecdh_free(&ctx);
- if (r != 0) {
- return SW_EXEC_ERROR();
- }
-
- sm_derive_all_keys(derived, olen);
-
- uint8_t *t = (uint8_t *)calloc(1, pubkey_len+16);
- memcpy(t, "\x7F\x49\x3F\x06\x0A", 5);
- if (sm_get_protocol() == MSE_AES)
- memcpy(t+5, OID_ID_CA_ECDH_AES_CBC_CMAC_128, 10);
- else if (sm_get_protocol() == MSE_3DES)
- memcpy(t+5, OID_ID_CA_ECDH_3DES_CBC_CBC, 10);
- t[15] = 0x86;
- memcpy(t+16, pubkey, pubkey_len);
-
- res_APDU[res_APDU_size++] = 0x7C;
- res_APDU[res_APDU_size++] = 20;
- res_APDU[res_APDU_size++] = 0x81;
- res_APDU[res_APDU_size++] = 8;
- memcpy(res_APDU+res_APDU_size, sm_get_nonce(), 8);
- res_APDU_size += 8;
- res_APDU[res_APDU_size++] = 0x82;
- res_APDU[res_APDU_size++] = 8;
-
- r = sm_sign(t, pubkey_len+16, res_APDU+res_APDU_size);
-
- free(t);
- if (r != CCID_OK)
- return SW_EXEC_ERROR();
- res_APDU_size += 8;
- }
- }
- return SW_OK();
-}
-
-int cmd_session_pin() {
- if (P1(apdu) == 0x01 && P2(apdu) == 0x81) {
- memcpy(sm_session_pin, random_bytes_get(8), 8);
- sm_session_pin_len = 8;
-
- memcpy(res_APDU, sm_session_pin, sm_session_pin_len);
- res_APDU_size = sm_session_pin_len;
- apdu.ne = sm_session_pin_len;
- }
- else
- return SW_INCORRECT_P1P2();
- return SW_OK();
-}
-
-int cmd_puk_auth() {
- uint8_t p1 = P1(apdu), p2 = P2(apdu);
- file_t *ef_puk = search_by_fid(EF_PUKAUT, NULL, SPECIFY_EF);
- if (!ef_puk || !ef_puk->data || file_get_size(ef_puk) == 0)
- return SW_FILE_NOT_FOUND();
- uint8_t *puk_data = file_get_data(ef_puk);
- if (apdu.nc > 0) {
- if (p1 == 0x0 || p1 == 0x1) {
- file_t *ef = NULL;
- if (p1 == 0x0) { /* Add */
- if (p2 != 0x0)
- return SW_INCORRECT_P1P2();
- for (int i = 0; i < puk_data[0]; i++) {
- ef = search_dynamic_file(EF_PUK+i);
- if (!ef) /* Never should not happen */
- return SW_MEMORY_FAILURE();
- if (ef->data == NULL || file_get_size(ef) == 0) /* found first empty slot */
- break;
- }
- uint8_t *tmp = (uint8_t *)calloc(file_get_size(ef_puk), sizeof(uint8_t));
- memcpy(tmp, puk_data, file_get_size(ef_puk));
- tmp[1] = puk_data[1]-1;
- flash_write_data_to_file(ef_puk, tmp, file_get_size(ef_puk));
- puk_data = file_get_data(ef_puk);
- free(tmp);
- }
- else if (p1 == 0x1) { /* Replace */
- if (p2 >= puk_data[0])
- return SW_INCORRECT_P1P2();
- ef = search_dynamic_file(EF_PUK+p2);
- if (!ef) /* Never should not happen */
- return SW_MEMORY_FAILURE();
- }
- flash_write_data_to_file(ef, apdu.data, apdu.nc);
- low_flash_available();
- }
- else
- return SW_INCORRECT_P1P2();
- }
- if (p1 == 0x2) {
- if (p2 >= puk_data[0])
- return SW_INCORRECT_P1P2();
- file_t *ef = search_dynamic_file(EF_PUK+p2);
- if (!ef)
- return SW_INCORRECT_P1P2();
- if (ef->data == NULL || file_get_size(ef) == 0)
- return SW_REFERENCE_NOT_FOUND();
- size_t chr_len = 0;
- const uint8_t *chr = cvc_get_chr(file_get_data(ef), file_get_size(ef), &chr_len);
- if (chr) {
- memcpy(res_APDU, chr, chr_len);
- res_APDU_size = chr_len;
- }
- return set_res_sw(0x90, puk_status[p2]);
- }
- else {
- memcpy(res_APDU, puk_data, 3);
- res_APDU[3] = 0;
- for (int i = 0; i < puk_data[0]; i++)
- res_APDU[3] += puk_status[i];
- res_APDU_size = 4;
- }
- return SW_OK();
-}
-
-int cmd_pso() {
- uint8_t p1 = P1(apdu), p2 = P2(apdu);
- if (p1 == 0x0 && (p2 == 0x92 || p2 == 0xAE || p2 == 0xBE)) { /* Verify certificate */
- if (apdu.nc == 0)
- return SW_WRONG_LENGTH();
- if (current_puk == NULL)
- return SW_REFERENCE_NOT_FOUND();
- if (apdu.data[0] != 0x7F || apdu.data[1] != 0x21) {
- uint8_t tlv_len = 2+format_tlv_len(apdu.nc, NULL);
- memmove(apdu.data+tlv_len, apdu.data, apdu.nc);
- memcpy(apdu.data, "\x7F\x21", 2);
- format_tlv_len(apdu.nc, apdu.data+2);
- apdu.nc += tlv_len;
- }
- int r = cvc_verify(apdu.data, apdu.nc, current_puk->cvcert, current_puk->cvcert_len);
- if (r != CCID_OK) {
- if (r == CCID_WRONG_DATA)
- return SW_DATA_INVALID();
- else if (r == CCID_WRONG_SIGNATURE)
- return SW_CONDITIONS_NOT_SATISFIED();
- return SW_EXEC_ERROR();
- }
- for (int i = 0; i < 0xfe; i++) {
- uint16_t fid = (CA_CERTIFICATE_PREFIX << 8) | i;
- file_t *ca_ef = search_dynamic_file(fid);
- if (!ca_ef) {
- ca_ef = file_new(fid);
- flash_write_data_to_file(ca_ef, apdu.data, apdu.nc);
- if (add_cert_puk_store(file_get_data(ca_ef), file_get_size(ca_ef), false) != CCID_OK)
- return SW_FILE_FULL();
-
- size_t chr_len = 0;
- const uint8_t *chr = cvc_get_chr(apdu.data, apdu.nc, &chr_len);
- if (chr == NULL)
- return SW_WRONG_DATA();
- size_t puk_len = 0, puk_bin_len = 0;
- const uint8_t *puk = cvc_get_pub(apdu.data, apdu.nc, &puk_len), *puk_bin = NULL;
- if (puk == NULL)
- return SW_WRONG_DATA();
- size_t oid_len = 0;
- const uint8_t *oid = cvc_get_field(puk, puk_len, &oid_len, 0x6);
- if (oid == NULL)
- return SW_WRONG_DATA();
- if (memcmp(oid, OID_ID_TA_RSA, 9) == 0) { //RSA
- puk_bin = cvc_get_field(puk, puk_len, &puk_bin_len, 0x81);
- if (!puk_bin)
- return SW_WRONG_DATA();
- }
- else if (memcmp(oid, OID_ID_TA_ECDSA, 9) == 0) { //ECC
- mbedtls_ecp_group_id ec_id = cvc_inherite_ec_group(apdu.data, apdu.nc);
- mbedtls_ecp_group grp;
- mbedtls_ecp_group_init(&grp);
- if (mbedtls_ecp_group_load(&grp, ec_id) != 0) {
- mbedtls_ecp_group_free(&grp);
- return SW_WRONG_DATA();
- }
- size_t plen = mbedtls_mpi_size(&grp.P);
- size_t t86_len = 0;
- const uint8_t *t86 = cvc_get_field(puk, puk_len, &t86_len, 0x86);
- if (mbedtls_ecp_get_type(&grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) {
- if (plen != t86_len) {
- mbedtls_ecp_group_free(&grp);
- return SW_WRONG_DATA();
- }
- puk_bin = t86;
- puk_bin_len = t86_len;
- }
- else if (mbedtls_ecp_get_type(&grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) {
- if (t86[0] == 0x2 || t86[0] == 0x3) {
- if (t86_len != plen+1) {
- mbedtls_ecp_group_free(&grp);
- return SW_WRONG_DATA();
- }
- }
- else if (t86[0] == 0x4) {
- if (t86_len != 2*plen+1) {
- mbedtls_ecp_group_free(&grp);
- return SW_WRONG_DATA();
- }
- }
- else {
- mbedtls_ecp_group_free(&grp);
- return SW_WRONG_DATA();
- }
- puk_bin = t86+1;
- puk_bin_len = plen;
- }
- mbedtls_ecp_group_free(&grp);
- if (!puk_bin)
- return SW_WRONG_DATA();
- }
- file_t *cd_ef = file_new((CD_PREFIX << 8) | i);
- size_t cd_len = asn1_build_cert_description(chr, chr_len, puk_bin, puk_bin_len, fid, NULL, 0);
- if (cd_len == 0)
- return SW_EXEC_ERROR();
- uint8_t *buf = (uint8_t *)calloc(cd_len, sizeof(uint8_t));
- int r = asn1_build_cert_description(chr, chr_len, puk_bin, puk_bin_len, fid, buf, cd_len);
- flash_write_data_to_file(cd_ef, buf, cd_len);
- free(buf);
- if (r == 0)
- return SW_EXEC_ERROR();
- low_flash_available();
- break;
- }
- }
- return SW_OK();
- }
- else
- return SW_INCORRECT_P1P2();
- return SW_OK();
-}
-
-int cmd_external_authenticate() {
- if (P1(apdu) != 0x0 || P2(apdu) != 0x0)
- return SW_INCORRECT_P1P2();
- if (ef_puk_aut == NULL)
- return SW_REFERENCE_NOT_FOUND();
- if (apdu.nc == 0)
- return SW_WRONG_LENGTH();
- file_t *ef_puk = search_by_fid(EF_PUKAUT, NULL, SPECIFY_EF);
- if (!ef_puk || !ef_puk->data || file_get_size(ef_puk) == 0)
- return SW_FILE_NOT_FOUND();
- uint8_t *puk_data = file_get_data(ef_puk);
- uint8_t *input = (uint8_t *)calloc(dev_name_len+challenge_len, sizeof(uint8_t)), hash[32];
- memcpy(input, dev_name, dev_name_len);
- memcpy(input+dev_name_len, challenge, challenge_len);
- hash256(input, dev_name_len+challenge_len, hash);
- int r = puk_verify(apdu.data, apdu.nc, hash, 32, file_get_data(ef_puk_aut), file_get_size(ef_puk_aut));
- free(input);
- if (r != 0)
- return SW_CONDITIONS_NOT_SATISFIED();
- puk_status[ef_puk_aut->fid & (MAX_PUK-1)] = 1;
- uint8_t auts = 0;
- for (int i = 0; i < puk_data[0]; i++)
- auts += puk_status[i];
- if (auts >= puk_data[2]) {
- isUserAuthenticated = true;
- }
- return SW_OK();
-}
-
typedef struct cmd
{
uint8_t ins;
diff --git a/src/hsm/sc_hsm.h b/src/hsm/sc_hsm.h
index ed9db07..a031f77 100644
--- a/src/hsm/sc_hsm.h
+++ b/src/hsm/sc_hsm.h
@@ -19,12 +19,14 @@
#define _SC_HSM_H_
#include
+#include "common.h"
+#include "mbedtls/rsa.h"
+#include "mbedtls/ecdsa.h"
#include "pico/stdlib.h"
#include "ccid2040.h"
extern const uint8_t sc_hsm_aid[];
-
#define ALGO_RSA_RAW 0x20 /* RSA signature with external padding */
#define ALGO_RSA_DECRYPT 0x21 /* RSA raw decrypt */
#define ALGO_RSA_DECRYPT_PKCS1 0x22
@@ -87,13 +89,32 @@ extern const uint8_t sc_hsm_aid[];
#define P15_KEYTYPE_ECC 0xA0
#define P15_KEYTYPE_AES 0xA8
+#define MAX_PUK 8
+
extern int pin_reset_retries(const file_t *pin, bool);
extern int pin_wrong_retry(const file_t *pin);
extern void hash(const uint8_t *input, size_t len, uint8_t output[32]);
extern void hash_multi(const uint8_t *input, size_t len, uint8_t output[32]);
extern void double_hash_pin(const uint8_t *pin, size_t len, uint8_t output[32]);
-
+extern uint16_t get_device_options();
+extern bool has_session_pin, has_session_sopin;
extern uint8_t session_pin[32], session_sopin[32];
+extern int check_pin(const file_t *pin, const uint8_t *data, size_t len);
+extern bool pka_enabled();
+extern const uint8_t *dev_name;
+extern size_t dev_name_len;
+extern uint8_t puk_status[MAX_PUK];
+extern int puk_store_select_chr(const uint8_t *chr);
+extern int delete_file(file_t *ef);
+extern const uint8_t *get_meta_tag(file_t *ef, uint16_t meta_tag, size_t *tag_len);
+extern bool key_has_purpose(file_t *ef, uint8_t purpose);
+extern int load_private_key_rsa(mbedtls_rsa_context *ctx, file_t *fkey);
+extern int load_private_key_ecdsa(mbedtls_ecdsa_context *ctx, file_t *fkey);
+extern bool wait_button();
+extern int store_keys(void *key_ctx, int type, uint8_t key_id);
+extern int find_and_store_meta_key(uint8_t key_id);
+extern uint32_t get_key_counter(file_t *fkey);
+extern uint32_t decrement_key_counter(file_t *fkey);
#endif
\ No newline at end of file