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