diff --git a/CMakeLists.txt b/CMakeLists.txt index b2db732..6847ba5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ target_sources(hsm2040 PUBLIC ${CMAKE_CURRENT_LIST_DIR}/debug.c ${CMAKE_CURRENT_LIST_DIR}/openpgp-do.c ${CMAKE_CURRENT_LIST_DIR}/ac.c + ${CMAKE_CURRENT_LIST_DIR}/file.c ${CMAKE_CURRENT_LIST_DIR}/flash.c ${CMAKE_CURRENT_LIST_DIR}/low_flash.c ${CMAKE_CURRENT_LIST_DIR}/call-rsa.c diff --git a/file.c b/file.c new file mode 100644 index 0000000..ba84cd9 --- /dev/null +++ b/file.c @@ -0,0 +1,154 @@ +#include "file.h" +#include "gnuk.h" +#include + +//puts FCI in the RAPDU +void process_fci(const file_t *pe) { + uint8_t *p = res_APDU; + uint8_t buf[64]; + res_APDU_size = 0; + res_APDU[res_APDU_size++] = 0x6f; + res_APDU[res_APDU_size++] = 0x00; //computed later + + res_APDU[res_APDU_size++] = 0x81; + res_APDU[res_APDU_size++] = 2; + if (pe->data) + memcpy(res_APDU+res_APDU_size, pe->data, 2); + else + memset(res_APDU+res_APDU_size, 0, 2); + res_APDU_size += 2; + + res_APDU[res_APDU_size++] = 0x82; + res_APDU[res_APDU_size++] = 1; + res_APDU[res_APDU_size] = 0; + if (pe->type == FILE_TYPE_INTERNAL_EF) + res_APDU[res_APDU_size++] |= 0x08; + else if (pe->type == FILE_TYPE_WORKING_EF) + res_APDU[res_APDU_size++] |= pe->ef_structure & 0x7; + else if (pe->type == FILE_TYPE_DF) + res_APDU[res_APDU_size++] |= 0x38; + + res_APDU[res_APDU_size++] = 0x83; + res_APDU[res_APDU_size++] = 2; + put_uint16_t(pe->fid, res_APDU+res_APDU_size); + res_APDU_size += 2; + res_APDU[1] = res_APDU_size-2; +} + +const uint8_t t[] = { + 0x01,0xbb, + 0x7F,0x21,0x82,0x01,0xB6,0x7F,0x4E,0x82,0x01,0x6E,0x5F,0x29,0x01,0x00,0x42,0x0E,0x44,0x45,0x43,0x56,0x43,0x41,0x65,0x49,0x44,0x30,0x30,0x31,0x30,0x32,0x7F,0x49,0x82,0x01,0x1D,0x06,0x0A,0x04,0x00,0x7F,0x00,0x07,0x02,0x02,0x02,0x02,0x03,0x81,0x20,0xA9,0xFB,0x57,0xDB,0xA1,0xEE,0xA9,0xBC,0x3E,0x66,0x0A,0x90,0x9D,0x83,0x8D,0x72,0x6E,0x3B,0xF6,0x23,0xD5,0x26,0x20,0x28,0x20,0x13,0x48,0x1D,0x1F,0x6E,0x53,0x77,0x82,0x20,0x7D,0x5A,0x09,0x75,0xFC,0x2C,0x30,0x57,0xEE,0xF6,0x75,0x30,0x41,0x7A,0xFF,0xE7,0xFB,0x80,0x55,0xC1,0x26,0xDC,0x5C,0x6C,0xE9,0x4A,0x4B,0x44,0xF3,0x30,0xB5,0xD9,0x83,0x20,0x26,0xDC,0x5C,0x6C,0xE9,0x4A,0x4B,0x44,0xF3,0x30,0xB5,0xD9,0xBB,0xD7,0x7C,0xBF,0x95,0x84,0x16,0x29,0x5C,0xF7,0xE1,0xCE,0x6B,0xCC,0xDC,0x18,0xFF,0x8C,0x07,0xB6,0x84,0x41,0x04,0x8B,0xD2,0xAE,0xB9,0xCB,0x7E,0x57,0xCB,0x2C,0x4B,0x48,0x2F,0xFC,0x81,0xB7,0xAF,0xB9,0xDE,0x27,0xE1,0xE3,0xBD,0x23,0xC2,0x3A,0x44,0x53,0xBD,0x9A,0xCE,0x32,0x62,0x54,0x7E,0xF8,0x35,0xC3,0xDA,0xC4,0xFD,0x97,0xF8,0x46,0x1A,0x14,0x61,0x1D,0xC9,0xC2,0x77,0x45,0x13,0x2D,0xED,0x8E,0x54,0x5C,0x1D,0x54,0xC7,0x2F,0x04,0x69,0x97,0x85,0x20,0xA9,0xFB,0x57,0xDB,0xA1,0xEE,0xA9,0xBC,0x3E,0x66,0x0A,0x90,0x9D,0x83,0x8D,0x71,0x8C,0x39,0x7A,0xA3,0xB5,0x61,0xA6,0xF7,0x90,0x1E,0x0E,0x82,0x97,0x48,0x56,0xA7,0x86,0x41,0x04,0x33,0x47,0xEC,0xF9,0x6F,0xFB,0x4B,0xD9,0xB8,0x55,0x4E,0xFB,0xCC,0xFC,0x7D,0x0B,0x24,0x2F,0x10,0x71,0xE2,0x9B,0x4C,0x9C,0x62,0x2C,0x79,0xE3,0x39,0xD8,0x40,0xAF,0x67,0xBE,0xB9,0xB9,0x12,0x69,0x22,0x65,0xD9,0xC1,0x6C,0x62,0x57,0x3F,0x45,0x79,0xFF,0xD4,0xDE,0x2D,0xE9,0x2B,0xAB,0x40,0x9D,0xD5,0xC5,0xD4,0x82,0x44,0xA9,0xF7,0x87,0x01,0x01,0x5F,0x20,0x0E,0x44,0x45,0x43,0x56,0x43,0x41,0x65,0x49,0x44,0x30,0x30,0x31,0x30,0x32,0x7F,0x4C,0x12,0x06,0x09,0x04,0x00,0x7F,0x00,0x07,0x03,0x01,0x02,0x02,0x53,0x05,0xFE,0x0F,0x01,0xFF,0xFF,0x5F,0x25,0x06,0x01,0x00,0x01,0x00,0x01,0x08,0x5F,0x24,0x06,0x01,0x03,0x01,0x00,0x01,0x08,0x5F,0x37,0x40,0x50,0x67,0x14,0x5C,0x68,0xCA,0xE9,0x52,0x0F,0x5B,0xB3,0x48,0x17,0xF1,0xCA,0x9C,0x43,0x59,0x3D,0xB5,0x64,0x06,0xC6,0xA3,0xB0,0x06,0xCB,0xF3,0xF3,0x14,0xE7,0x34,0x9A,0xCF,0x0C,0xC6,0xBF,0xEB,0xCB,0xDE,0xFD,0x10,0xB4,0xDC,0xF0,0xF2,0x31,0xDA,0x56,0x97,0x7D,0x88,0xF9,0xF9,0x01,0x82,0xD1,0x99,0x07,0x6A,0x56,0x50,0x64,0x51 +}; +const uint8_t token_info[] = { + 0x0, 0x1f, + 0x30, 0x1d, 0x2, 0x1, 0x2, 0x4, 0x4, 0x6, 0x0, 0x0, 0x0, 0xc, 0x6, 0x4d, 0x61, 0x6e, 0x75, 0x49, 0x44, 0x80, 0x6, 0x50, 0x61, 0x74, 0x61, 0x74, 0x61, 0x3, 0x2, 0x7, 0x80 +}; + +const file_t file_entries[] = { + { .fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = {0} }, // MF + { .fid = 0x2f00, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.DIR + { .fid = 0x2f01, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.ATR + { .fid = 0x2f02, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF,.data = (uint8_t *)t, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.GDO + { .fid = 0x2f03, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF,.data = (uint8_t *)token_info, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.TokenInfo + { .fid = 0x5015, .parent = 0, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = {0} }, //DF.PKCS15 + { .fid = 0x5031, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.ODF + { .fid = 0x5032, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.TokenInfo + { .fid = 0x5033, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.UnusedSpace + { .fid = 0x0000, .parent = 0, .name = openpgpcard_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, + { .fid = 0x0000, .parent = 0, .name = sc_hsm_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, + { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end +}; + +const file_t *MF = &file_entries[0]; +const file_t *file_last = &file_entries[sizeof(file_entries)/sizeof(file_t)-1]; +const file_t *file_openpgp = &file_entries[sizeof(file_entries)/sizeof(file_t)-3]; +const file_t *file_sc_hsm = &file_entries[sizeof(file_entries)/sizeof(file_t)-2]; + +bool card_terminated = false; + +bool is_parent(const file_t *child, const file_t *parent) { + if (child == parent) + return true; + if (child == MF) + return false; + return is_parent(&file_entries[child->parent], parent); +} + +const file_t *search_by_name(uint8_t *name, uint16_t namelen) { + for (const file_t *p = file_entries; p != file_last; p++) { + if (p->name && *p->name == apdu.cmd_apdu_data_len && memcmp(p->name+1, name, namelen) == 0) { + return p; + } + } + return NULL; +} + +const file_t *search_by_fid(const uint16_t fid, const file_t *parent, const uint8_t sp) { + + for (const file_t *p = file_entries; p != file_last; p++) { + if (p->fid != 0x0000 && p->fid == fid) { + if (!parent || (parent && is_parent(p, parent))) { + if (!sp || sp == SPECIFY_ANY || (((sp & SPECIFY_EF) && (p->type & FILE_TYPE_INTERNAL_EF)) || ((sp & SPECIFY_DF) && p->type == FILE_TYPE_DF))) + return p; + } + } + } + return NULL; +} + +uint8_t make_path_buf(const file_t *pe, uint8_t *buf, uint8_t buflen, const file_t *top) { + if (!buflen) + return 0; + if (pe == top) //MF or relative DF + return 0; + put_uint16_t(pe->fid, buf); + return make_path_buf(&file_entries[pe->parent], buf+2, buflen-2, top)+2; +} + +uint8_t make_path(const file_t *pe, const file_t *top, uint8_t *path) { + uint8_t buf[MAX_DEPTH*2], *p = path; + put_uint16_t(pe->fid, buf); + uint8_t depth = make_path_buf(&file_entries[pe->parent], buf+2, sizeof(buf)-2, top)+2; + for (int d = depth-2; d >= 0; d -= 2) { + memcpy(p, buf+d, 2); + p += 2; + } + return depth; +} + +const file_t *search_by_path(const uint8_t *pe_path, uint8_t pathlen, const file_t *parent) { + uint8_t path[MAX_DEPTH*2]; + if (pathlen > sizeof(path)) { + return NULL; + } + for (const file_t *p = file_entries; p != file_last; p++) { + uint8_t depth = make_path(p, parent, path); + if (pathlen == depth && memcmp(path, pe_path, depth)) + return p; + } + return NULL; +} + +uint8_t file_selection; +const file_t *currentEF = NULL; +const file_t *currentDF = NULL; +const file_t *selected_applet = NULL; +bool isUserAuthenticated = false; + +bool authenticate_action(const file_t *ef, uint8_t op) { + uint8_t acl = ef->acl[op]; + if (acl == 0x0) + return true; + else if (acl == 0xff) + return false; + else if (acl == 0x90 || acl & 0x9F == 0x10) { + // PIN required. + if(isUserAuthenticated) { + return true; + } + else { + return false; + } + } + return false; +} diff --git a/file.h b/file.h new file mode 100644 index 0000000..6bd2a6f --- /dev/null +++ b/file.h @@ -0,0 +1,67 @@ +#ifndef _FILE_H_ +#define _FILE_H_ + +#include +#include "pico/stdlib.h" + +#define FILE_TYPE_UNKNOWN 0x00 +#define FILE_TYPE_DF 0x04 +#define FILE_TYPE_INTERNAL_EF 0x03 +#define FILE_TYPE_WORKING_EF 0x01 +#define FILE_TYPE_BSO 0x10 + +/* EF structures */ +#define FILE_EF_UNKNOWN 0x00 +#define FILE_EF_TRANSPARENT 0x01 +#define FILE_EF_LINEAR_FIXED 0x02 +#define FILE_EF_LINEAR_FIXED_TLV 0x03 +#define FILE_EF_LINEAR_VARIABLE 0x04 +#define FILE_EF_LINEAR_VARIABLE_TLV 0x05 +#define FILE_EF_CYCLIC 0x06 +#define FILE_EF_CYCLIC_TLV 0x07 + +#define ACL_OP_DELETE_SELF 0x00 +#define ACL_OP_CREATE_DF 0x01 +#define ACL_OP_CREATE_EF 0x02 +#define ACL_OP_DELETE_CHILD 0x03 +#define ACL_OP_WRITE 0x04 +#define ACL_OP_UPDATE_ERASE 0x05 +#define ACL_OP_READ_SEARCH 0x06 + +#define SPECIFY_EF 0x1 +#define SPECIFY_DF 0x2 +#define SPECIFY_ANY 0x3 + +#define MAX_DEPTH 4 + +typedef struct file +{ + const uint16_t fid; + const uint8_t parent; //entry number in the whole table!! + const uint8_t *name; + const uint8_t type; + uint8_t *data; //should include 2 bytes len at begining + const uint8_t ef_structure; + const uint8_t acl[7]; +} file_t; + +extern const file_t *currentEF; +extern const file_t *currentDF; +extern const file_t *selected_applet; + +extern const file_t *MF; +extern const file_t *file_last; +extern const file_t *file_openpgp; +extern const file_t *file_sc_hsm; +extern bool card_terminated; + +extern const file_t *search_by_fid(const uint16_t fid, const file_t *parent, const uint8_t sp); +extern const file_t *search_by_name(uint8_t *name, uint16_t namelen); +extern const file_t *search_by_path(const uint8_t *pe_path, uint8_t pathlen, const file_t *parent); +extern bool authenticate_action(const file_t *ef, uint8_t op); +extern void process_fci(const file_t *pe); + +extern const file_t file_entries[]; + +#endif + diff --git a/flash.c b/flash.c index 65eef1a..4cef70f 100644 --- a/flash.c +++ b/flash.c @@ -42,169 +42,24 @@ #include "hardware/flash.h" #include "hsm2040.h" #include "tusb.h" - -extern void low_flash_available(); - -/* - * Flash memory map - * - * _text - * .text - * .ctors - * .dtors - * _etext - * .data - * _bss_start - * .bss - * _end - * - * ch_certificate_startp - * <2048 bytes> - * _keystore_pool - * Three flash pages for keystore - * a page contains a key data of: - * For RSA-2048: 512-byte (p, q and N) - * For RSA-4096: 1024-byte (p, q and N) - * For ECDSA/ECDH and EdDSA, there are padding after public key - * _data_pool - * - */ - -#define FLASH_DATA_POOL_HEADER_SIZE 2 -#define FLASH_DATA_POOL_SIZE (2048*1024) - -static uint16_t flash_page_size; -static const uint8_t *data_pool; -static uint8_t *last_p; - -/* The first halfword is generation for the data page (little endian) */ -const uint8_t flash_data[4] __attribute__ ((section (".gnuk_data"))) = { - 0x00, 0x00, 0xff, 0xff -}; +#include "file.h" #define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES >> 1) // DATA starts at the mid of flash +#define FLASH_DATA_HEADER_SIZE (sizeof(uintptr_t)+sizeof(uint32_t)) +//To avoid possible future allocations, data region starts at the begining of flash and goes upwards to the center region -const uint8_t *flash_addr_key_storage_start = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET); -const uint8_t *flash_addr_data_storage_start = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET + 2048 * 1024); // 2 MB -const uint8_t *ch_certificate_start = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET - FLASH_SECTOR_SIZE); -#define FLASH_ADDR_KEY_STORAGE_START flash_addr_key_storage_start -#define FLASH_ADDR_DATA_STORAGE_START flash_addr_data_storage_start +const uintptr_t start_data_pool = (XIP_BASE + FLASH_TARGET_OFFSET); +const uintptr_t end_data_pool = (XIP_BASE + PICO_FLASH_SIZE_BYTES)-FLASH_DATA_HEADER_SIZE; //This is a fixed value. DO NOT CHANGE +#define FLASH_ADDR_DATA_STORAGE_START start_data_pool extern int flash_erase_page (uintptr_t addr); extern int flash_program_halfword (uintptr_t addr, uint16_t data); +int flash_program_block(uintptr_t addr, const uint8_t *data, size_t len) ; extern int flash_check_blank (const uint8_t *p_start, size_t size); extern int flash_write (uintptr_t dst_addr, const uint8_t *src, size_t len); -static int key_available_at (const uint8_t *k, int key_size) -{ - int i; - - for (i = 0; i < key_size; i++) - if (k[i]) - break; - if (i == key_size) /* It's ZERO. Released key. */ - return 0; - - for (i = 0; i < key_size; i++) - if (k[i] != 0xff) - break; - if (i == key_size) /* It's FULL. Unused key. */ - return 0; - - return 1; -} - -void -flash_do_storage_init (const uint8_t **p_do_start, const uint8_t **p_do_end) -{ - uint16_t gen0, gen1; - uint16_t *gen0_p = (uint16_t *)FLASH_ADDR_DATA_STORAGE_START; - uint16_t *gen1_p; - - flash_page_size = FLASH_SECTOR_SIZE * 8; // 32 KB - - gen1_p = (uint16_t *)(FLASH_ADDR_DATA_STORAGE_START + flash_page_size); - data_pool = FLASH_ADDR_DATA_STORAGE_START; - - /* Check data pool generation and choose the page */ - gen0 = *gen0_p; - gen1 = *gen1_p; - - if (gen0 == 0xffff && gen1 == 0xffff) - { - gen0 = 0x0000; - *gen0_p = gen0; - } - - if (gen0 == 0xffff) - /* Use another page if a page is erased. */ - data_pool = FLASH_ADDR_DATA_STORAGE_START + flash_page_size; - else if (gen1 == 0xffff) - /* Or use different page if another page is erased. */ - data_pool = FLASH_ADDR_DATA_STORAGE_START; - else if ((gen0 == 0xfffe && gen1 == 0) || gen1 > gen0) - /* When both pages have valid header, use newer page. */ - data_pool = FLASH_ADDR_DATA_STORAGE_START + flash_page_size; - - *p_do_start = data_pool + FLASH_DATA_POOL_HEADER_SIZE; - *p_do_end = data_pool + flash_page_size; -} - -static uint8_t *flash_key_getpage (enum kind_of_key kk); - -void -flash_terminate (void) -{ - int i; - - for (i = 0; i < 3; i++) - flash_erase_page ((uintptr_t)flash_key_getpage (i)); - flash_erase_page ((uintptr_t)FLASH_ADDR_DATA_STORAGE_START); - flash_erase_page ((uintptr_t)(FLASH_ADDR_DATA_STORAGE_START + flash_page_size)); - data_pool = FLASH_ADDR_DATA_STORAGE_START; - last_p = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START + FLASH_DATA_POOL_HEADER_SIZE; -#if defined(CERTDO_SUPPORT) - flash_erase_page ((uintptr_t)ch_certificate_start); - if (FLASH_CH_CERTIFICATE_SIZE > flash_page_size) - flash_erase_page ((uintptr_t)(ch_certificate_start + flash_page_size)); -#endif -} - -void -flash_activate (void) -{ - flash_program_halfword ((uintptr_t)FLASH_ADDR_DATA_STORAGE_START, 0); - low_flash_available(); -} - - -void -flash_key_storage_init (void) -{ - const uint8_t *p; - int i; - - /* For each key, find its address. */ - p = FLASH_ADDR_KEY_STORAGE_START; - for (i = 0; i < 3; i++) - { - const uint8_t *k; - int key_size = gpg_get_algo_attr_key_size (i, GPG_KEY_STORAGE); - - kd[i].pubkey = NULL; - for (k = p; k < p + flash_page_size; k += key_size) - if (key_available_at (k, key_size)) - { - int prv_len = gpg_get_algo_attr_key_size (i, GPG_KEY_PRIVATE); - - kd[i].pubkey = k + prv_len; - break; - } - - p += flash_page_size; - } -} +void low_flash_available(); /* * Flash data pool managenent @@ -229,535 +84,80 @@ flash_key_storage_init (void) * PAD: optional byte for 16-bit alignment */ -void -flash_set_data_pool_last (const uint8_t *p) -{ - last_p = (uint8_t *)p; -} - -/* - * We use two pages - */ -static int -flash_copying_gc (void) -{ - uint8_t *src, *dst; - uint16_t generation; - - if (data_pool == FLASH_ADDR_DATA_STORAGE_START) - { - src = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START; - dst = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START + flash_page_size; +uintptr_t allocate_free_addr(uint16_t size) { + if (size > FLASH_SECTOR_SIZE) + return 0x0; //ERROR + size_t real_size = size+sizeof(uint16_t)+sizeof(uintptr_t)+sizeof(uint16_t); //len+len size+next address+fid + for (uintptr_t base = end_data_pool; base >= start_data_pool; base = *(uintptr_t *)base) { + uintptr_t addr_alg = base & -FLASH_SECTOR_SIZE; //start address of sector + uintptr_t potential_addr = base-real_size; + if (*(uintptr_t *)base == 0x0) { //we are at the end + //now we check if we fit in the current sector + if (addr_alg <= potential_addr) //it fits in the current sector + { + *(uintptr_t *)potential_addr = 0x0; + *(uintptr_t *)base = potential_addr; + return potential_addr; + } + else if (addr_alg-FLASH_SECTOR_SIZE >= start_data_pool) { //check whether it fits in the next sector, so we take addr_aligned as the base + potential_addr = addr_alg-real_size; + *(uintptr_t *)potential_addr = 0x0; + *(uintptr_t *)base = potential_addr; + return potential_addr; + } + return 0x0; + } + //we check if |base-(next_addr+size_next_addr)| >= |base-potential_addr| + else if (base-(*(uintptr_t *)base+*(uint16_t *)(*(uintptr_t *)base+sizeof(uintptr_t)+sizeof(uint16_t))) >= base-potential_addr && addr_alg <= potential_addr) { + *(uintptr_t *)potential_addr = *(uintptr_t *)base; + *(uintptr_t *)base = potential_addr; + return potential_addr; + } } - else - { - src = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START + flash_page_size; - dst = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START; - } - - generation = *(uint16_t *)src; - data_pool = dst; - gpg_data_copy (data_pool + FLASH_DATA_POOL_HEADER_SIZE); - if (generation == 0xfffe) - generation = 0; - else - generation++; - flash_program_halfword ((uintptr_t)dst, generation); - flash_erase_page ((uintptr_t)src); - low_flash_available(); - return 0; + return 0x0; //probably never reached } -static int -is_data_pool_full (size_t size) -{ - return last_p + size > data_pool + flash_page_size; -} - -static uint8_t * -flash_data_pool_allocate (size_t size) -{ - uint8_t *p; - - size = (size + 1) & ~1; /* allocation unit is 1-halfword (2-byte) */ - - if (is_data_pool_full (size)) - if (flash_copying_gc () < 0 || /*still*/ is_data_pool_full (size)) - TU_LOG1 ("!!!! FATAL: %d\r\n",FATAL_FLASH); - - p = last_p; - last_p += size; - return p; -} - -void -flash_do_write_internal (const uint8_t *p, int nr, const uint8_t *data, int len) -{ - uint16_t hw; - uintptr_t addr; - int i; - - addr = (uintptr_t)p; - hw = nr | (len << 8); - if (flash_program_halfword (addr, hw) != 0) - flash_warning ("DO WRITE ERROR"); - addr += 2; - - for (i = 0; i < len/2; i++) - { - hw = data[i*2] | (data[i*2+1]<<8); - if (flash_program_halfword (addr, hw) != 0) - flash_warning ("DO WRITE ERROR"); - addr += 2; - } - - if ((len & 1)) - { - hw = data[i*2] | 0xff00; - if (flash_program_halfword (addr, hw) != 0) - flash_warning ("DO WRITE ERROR"); - } - - low_flash_available(); -} - -const uint8_t * -flash_do_write (uint8_t nr, const uint8_t *data, int len) -{ - const uint8_t *p; - - DEBUG_INFO ("flash DO\r\n"); - - p = flash_data_pool_allocate (2 + len); - if (p == NULL) - { - DEBUG_INFO ("flash data pool allocation failure.\r\n"); - return NULL; - } - - flash_do_write_internal (p, nr, data, len); - DEBUG_INFO ("flash DO...done\r\n"); - return p + 1; -} - -void -flash_warning (const char *msg) -{ - (void)msg; - DEBUG_INFO ("FLASH: "); - DEBUG_INFO (msg); - DEBUG_INFO ("\r\n"); -} - -void -flash_do_release (const uint8_t *do_data) -{ - uintptr_t addr = (uintptr_t)do_data - 1; - uintptr_t addr_tag = addr; - int i; - int len = do_data[0]; - - /* Don't filling zero for data in code (such as ds_count_initial_value) */ - if (do_data < FLASH_ADDR_DATA_STORAGE_START - || do_data > FLASH_ADDR_DATA_STORAGE_START + FLASH_DATA_POOL_SIZE) - return; - - addr += 2; - - /* Fill zero for content and pad */ - for (i = 0; i < len/2; i ++) - { - if (flash_program_halfword (addr, 0) != 0) - flash_warning ("fill-zero failure"); - addr += 2; - } - - if ((len & 1)) - { - if (flash_program_halfword (addr, 0) != 0) - flash_warning ("fill-zero pad failure"); - } - - /* Fill 0x0000 for "tag_number and length" word */ - if (flash_program_halfword (addr_tag, 0) != 0) - flash_warning ("fill-zero tag_nr failure"); - - //CAUTION: flash_do_release is followed by a flash_write. Thus, we can avoid a single write - //low_flash_available(); -} - - -static uint8_t * -flash_key_getpage (enum kind_of_key kk) -{ - /* There is a page for each KK. */ - return (uint8_t *)FLASH_ADDR_KEY_STORAGE_START + (flash_page_size * kk); -} - -uint8_t * -flash_key_alloc (enum kind_of_key kk) -{ - uint8_t *k, *k0 = flash_key_getpage (kk); - int i; - int key_size = gpg_get_algo_attr_key_size (kk, GPG_KEY_STORAGE); - - /* Seek free space in the page. */ - for (k = k0; k < k0 + flash_page_size; k += key_size) - { - const uint32_t *p = (const uint32_t *)k; - - for (i = 0; i < key_size/4; i++) - if (p[i] != 0xffffffff) - break; - - if (i == key_size/4) /* Yes, it's empty. */ - return k; - } - - /* Should not happen as we have enough free space all time, but just - in case. */ - return NULL; -} - -int -flash_key_write (uint8_t *key_addr, - const uint8_t *key_data, int key_data_len, - const uint8_t *pubkey, int pubkey_len) -{ - uint16_t hw; - uintptr_t addr; - int i; - - addr = (uintptr_t)key_addr; - for (i = 0; i < key_data_len/2; i ++) - { - hw = key_data[i*2] | (key_data[i*2+1]<<8); - if (flash_program_halfword (addr, hw) != 0) - return -1; - addr += 2; - } - - for (i = 0; i < pubkey_len/2; i ++) - { - hw = pubkey[i*2] | (pubkey[i*2+1]<<8); - if (flash_program_halfword (addr, hw) != 0) - return -1; - addr += 2; - } - - low_flash_available(); - return 0; -} - -static int -flash_check_all_other_keys_released (const uint8_t *key_addr, int key_size) -{ - uintptr_t start = (uintptr_t)key_addr & ~(flash_page_size - 1); - const uint32_t *p = (const uint32_t *)start; - - while (p < (const uint32_t *)(start + flash_page_size)) - if (p == (const uint32_t *)key_addr) - p += key_size/4; - else - if (*p) - return 0; - else - p++; - - return 1; -} - -static void -flash_key_fill_zero_as_released (uint8_t *key_addr, int key_size) -{ - int i; - uintptr_t addr = (uintptr_t)key_addr; - - for (i = 0; i < key_size/2; i++) - flash_program_halfword (addr + i*2, 0); - - low_flash_available(); -} - -void -flash_key_release (uint8_t *key_addr, int key_size) -{ - if (flash_check_all_other_keys_released (key_addr, key_size)) - flash_erase_page (((uintptr_t)key_addr & ~(flash_page_size - 1))); - else - flash_key_fill_zero_as_released (key_addr, key_size); -} - -void -flash_key_release_page (enum kind_of_key kk) -{ - flash_erase_page ((uintptr_t)flash_key_getpage (kk)); -} - - -void -flash_clear_halfword (uintptr_t addr) -{ - flash_program_halfword (addr, 0); - low_flash_available(); -} - - -void -flash_put_data_internal (const uint8_t *p, uint16_t hw) -{ - flash_program_halfword ((uintptr_t)p, hw); - low_flash_available(); -} - -void -flash_put_data (uint16_t hw) -{ - uint8_t *p; - - p = flash_data_pool_allocate (2); - if (p == NULL) - { - DEBUG_INFO ("data allocation failure.\r\n"); - } - - flash_program_halfword ((uintptr_t)p, hw); - low_flash_available(); -} - - -void -flash_bool_clear (const uint8_t **addr_p) -{ - const uint8_t *p; - - if ((p = *addr_p) == NULL) - return; - - flash_program_halfword ((uintptr_t)p, 0); - *addr_p = NULL; - low_flash_available(); -} - -void -flash_bool_write_internal (const uint8_t *p, int nr) -{ - flash_program_halfword ((uintptr_t)p, nr); - low_flash_available(); -} - -const uint8_t * -flash_bool_write (uint8_t nr) -{ - uint8_t *p; - uint16_t hw = nr; - - p = flash_data_pool_allocate (2); - if (p == NULL) - { - DEBUG_INFO ("bool allocation failure.\r\n"); - return NULL; - } - - flash_program_halfword ((uintptr_t)p, hw); - low_flash_available(); - return p; -} - - -void -flash_enum_clear (const uint8_t **addr_p) -{ - flash_bool_clear (addr_p); -} - -void -flash_enum_write_internal (const uint8_t *p, int nr, uint8_t v) -{ - uint16_t hw = nr | (v << 8); - - flash_program_halfword ((uintptr_t)p, hw); - low_flash_available(); -} - -const uint8_t * -flash_enum_write (uint8_t nr, uint8_t v) -{ - uint8_t *p; - uint16_t hw = nr | (v << 8); - - p = flash_data_pool_allocate (2); - if (p == NULL) - { - DEBUG_INFO ("enum allocation failure.\r\n"); - return NULL; - } - - flash_program_halfword ((uintptr_t)p, hw); - low_flash_available(); - return p; -} - - -int -flash_cnt123_get_value (const uint8_t *p) -{ - if (p == NULL) +int flash_clear_file(file_t *file) { + uintptr_t prev_addr = (uintptr_t)(file->data+*(uint16_t *)file->data); + uintptr_t base_addr = (uintptr_t)file->data-sizeof(uintptr_t); + uintptr_t next_addr = *(uintptr_t *)base_addr; + *(uintptr_t *)prev_addr = next_addr; + *(uint16_t *)file->data = 0; return 0; - else - { - uint8_t v = *p; - - /* - * After erase, a halfword in flash memory becomes 0xffff. - * The halfword can be programmed to any value. - * Then, the halfword can be programmed to zero. - * - * Thus, we can represent value 1, 2, and 3. - */ - if (v == 0xff) - return 1; - else if (v == 0x00) - return 3; - else - return 2; - } } -void -flash_cnt123_write_internal (const uint8_t *p, int which, int v) +int flash_write_data_to_file(file_t *file, const uint8_t *data, uint16_t len) { + if (len > FLASH_SECTOR_SIZE) + return 1; + if (file->data) { //already in flash + uint16_t size_file_flash = *(uint16_t *)file->data; + if (len <= size_file_flash) { //it fits, no need to move it + flash_program_halfword((uintptr_t)file->data, len); + flash_program_block((uintptr_t)file->data+sizeof(uint16_t), data, len); + low_flash_available(); + return 0; + } + else { //we clear the old file + flash_clear_file(file); + } + } + uintptr_t new_addr = allocate_free_addr(len); + if (new_addr == 0x0) + return 2; + file->data = (uint8_t *)new_addr+sizeof(uintptr_t)+sizeof(uint16_t); //next addr+fid + flash_program_halfword(new_addr+sizeof(uintptr_t), file->fid); + flash_program_halfword((uintptr_t)file->data, len); + flash_program_block((uintptr_t)file->data+sizeof(uint16_t), data, len); + low_flash_available(); + return 0; +} + +void flash_warning (const char *msg) { - uint16_t hw; - - hw = NR_COUNTER_123 | (which << 8); - flash_program_halfword ((uintptr_t)p, hw); - - if (v == 1) - return; - else if (v == 2) - flash_program_halfword ((uintptr_t)p+2, 0xc3c3); - else /* v == 3 */ - flash_program_halfword ((uintptr_t)p+2, 0); - low_flash_available(); -} - -void -flash_cnt123_increment (uint8_t which, const uint8_t **addr_p) -{ - const uint8_t *p; - uint16_t hw; - - if ((p = *addr_p) == NULL) - { - p = flash_data_pool_allocate (4); - if (p == NULL) - { - DEBUG_INFO ("cnt123 allocation failure.\r\n"); - return; - } - hw = NR_COUNTER_123 | (which << 8); - flash_program_halfword ((uintptr_t)p, hw); - *addr_p = p + 2; - } - else - { - uint8_t v = *p; - - if (v == 0) - return; - - if (v == 0xff) - hw = 0xc3c3; - else - hw = 0; - - flash_program_halfword ((uintptr_t)p, hw); - } - low_flash_available(); -} - -void -flash_cnt123_clear (const uint8_t **addr_p) -{ - const uint8_t *p; - - if ((p = *addr_p) == NULL) - return; - - flash_program_halfword ((uintptr_t)p, 0); - p -= 2; - flash_program_halfword ((uintptr_t)p, 0); - *addr_p = NULL; - low_flash_available(); + (void)msg; + DEBUG_INFO ("FLASH: "); + DEBUG_INFO (msg); + DEBUG_INFO ("\r\n"); } -#if defined(CERTDO_SUPPORT) -int -flash_erase_binary (uint8_t file_id) -{ - if (file_id == FILEID_CH_CERTIFICATE) - { - const uint8_t *p = ch_certificate_start; - if (flash_check_blank (p, FLASH_CH_CERTIFICATE_SIZE) == 0) - { - flash_erase_page ((uintptr_t)p); - if (FLASH_CH_CERTIFICATE_SIZE > flash_page_size) - flash_erase_page ((uintptr_t)p + flash_page_size); - } - low_flash_available(); - - return 0; - } - - return -1; -} -#endif - - -int -flash_write_binary (uint8_t file_id, const uint8_t *data, - uint16_t len, uint16_t offset) -{ - uint16_t maxsize; - const uint8_t *p; - - if (file_id == FILEID_SERIAL_NO) - { - maxsize = 6; - p = &openpgpcard_aid[8]; - } -#if defined(CERTDO_SUPPORT) - else if (file_id == FILEID_CH_CERTIFICATE) - { - maxsize = FLASH_CH_CERTIFICATE_SIZE; - p = ch_certificate_start; - } -#endif - else - return -1; - - if (offset + len > maxsize || (offset&1) || (len&1)) - return -1; - else - { - uint16_t hw; - uintptr_t addr; - int i; - - if (flash_check_blank (p + offset, len) == 0) - return -1; - - addr = (uintptr_t)p + offset; - for (i = 0; i < len/2; i++) - { - hw = data[i*2] | (data[i*2+1]<<8); - if (flash_program_halfword (addr, hw) != 0) - flash_warning ("DO WRITE ERROR"); - addr += 2; - } - - low_flash_available(); - return 0; - } -} diff --git a/gnuk.h b/gnuk.h index 29121ca..6e7d8a5 100644 --- a/gnuk.h +++ b/gnuk.h @@ -1,3 +1,6 @@ +#ifndef _GNUK_H_ +#define _GNUK_H_ + #include "config.h" /* * Application layer <-> CCID layer data structure @@ -8,8 +11,8 @@ struct apdu { /* command APDU */ uint8_t *cmd_apdu_head; /* CLS INS P1 P2 [ internal Lc ] */ uint8_t *cmd_apdu_data; - uint16_t cmd_apdu_data_len; /* Nc, calculated by Lc field */ - uint16_t expected_res_size; /* Ne, calculated by Le field */ + size_t cmd_apdu_data_len; /* Nc, calculated by Lc field */ + size_t expected_res_size; /* Ne, calculated by Le field */ /* response APDU */ uint16_t sw; @@ -17,6 +20,12 @@ struct apdu { uint8_t *res_apdu_data; }; + +#define CLS(a) a.cmd_apdu_head[0] +#define INS(a) a.cmd_apdu_head[1] +#define P1(a) a.cmd_apdu_head[2] +#define P2(a) a.cmd_apdu_head[3] + extern struct apdu apdu; #define CARD_CHANGE_INSERT 0 @@ -98,7 +107,7 @@ void ac_reset_admin (void); void ac_fini (void); -void set_res_sw (uint8_t sw1, uint8_t sw2); +uint16_t set_res_sw (uint8_t sw1, uint8_t sw2); extern uint8_t file_selection; extern const uint8_t historical_bytes[]; extern uint16_t data_objects_number_of_bytes; @@ -421,7 +430,8 @@ extern uint8_t admin_authorized; #define OPENPGP_CARD_INITIAL_PW3 "12345678" #endif -extern const uint8_t openpgpcard_aid[14]; +extern const uint8_t openpgpcard_aid[]; +extern const uint8_t sc_hsm_aid[]; void flash_bool_clear (const uint8_t **addr_p); const uint8_t *flash_bool_write (uint8_t nr); @@ -500,3 +510,17 @@ unique_device_id (void) return id; } +static inline const uint16_t make_uint16_t(uint8_t b1, uint8_t b2) { + return (b1 << 8) | b2; +} +static inline const uint16_t get_uint16_t(uint8_t *b, uint16_t offset) { + return make_uint16_t(b[offset], b[offset+1]); +} +static inline const void put_uint16_t(uint16_t n, uint8_t *b) { + *b++ = (n >> 8) & 0xff; + *b = n & 0xff; +} + + + +#endif \ No newline at end of file diff --git a/hsm2040.c b/hsm2040.c index 3335887..3053706 100644 --- a/hsm2040.c +++ b/hsm2040.c @@ -385,6 +385,7 @@ void usb_tx_enable(const void *buf, uint32_t len) */ static const uint8_t ATR_head[] = { 0x3b, 0xda, 0x11, 0xff, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0x03, + //0x3B,0xFE,0x18,0x00,0x00,0x81,0x31,0xFE,0x45,0x80,0x31,0x81,0x54,0x48,0x53,0x4D,0x31,0x73,0x80,0x21,0x40,0x81,0x07,0xFA }; /* Send back ATR (Answer To Reset) */ @@ -393,7 +394,14 @@ static enum ccid_state ccid_power_on (struct ccid *c) TU_LOG1("!!! CCID POWER ON %d\r\n",c->application); uint8_t p[CCID_MSG_HEADER_SIZE+1]; /* >= size of historical_bytes -1 */ int hist_len = historical_bytes[0]; - size_t size_atr = sizeof (ATR_head) + hist_len + 1; + + char atr_sc_hsm[] = { 0x3B,0x8E,0x80,0x01,0x80,0x31,0x81,0x54,0x48,0x53,0x4D,0x31,0x73,0x80,0x21,0x40,0x81,0x07,0x18 }; + uint8_t mode = 1; //1 sc-hsm, 0 openpgp + size_t size_atr; + if (mode == 1) + size_atr = sizeof(atr_sc_hsm); + else + size_atr = sizeof (ATR_head) + hist_len + 1; uint8_t xor_check = 0; int i; if (c->application == 0) @@ -416,20 +424,26 @@ static enum ccid_state ccid_power_on (struct ccid *c) p[CCID_MSG_CHAIN_OFFSET] = 0x00; memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE); - memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, ATR_head, sizeof (ATR_head)); + if (mode == 1) + { + memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE, atr_sc_hsm, sizeof(atr_sc_hsm)); + } + else + { + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, ATR_head, sizeof (ATR_head)); - for (i = 1; i < (int)sizeof (ATR_head); i++) - xor_check ^= ATR_head[i]; - memcpy (p, historical_bytes + 1, hist_len); + for (i = 1; i < (int)sizeof (ATR_head); i++) + xor_check ^= ATR_head[i]; + memcpy (p, historical_bytes + 1, hist_len); #ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT - if (file_selection == 255) - p[7] = 0x03; + if (file_selection == 255) + p[7] = 0x03; #endif - for (i = 0; i < hist_len; i++) - xor_check ^= p[i]; - p[i] = xor_check; - memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE+sizeof (ATR_head), p, hist_len+1); - + for (i = 0; i < hist_len; i++) + xor_check ^= p[i]; + p[i] = xor_check; + memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE+sizeof (ATR_head), p, hist_len+1); + } /* This is a single packet Bulk-IN transaction */ c->epi->buf = NULL; @@ -1112,26 +1126,54 @@ static int end_cmd_apdu_data (struct ep_out *epo, size_t orig_len) if (CMD_APDU_HEAD_SIZE + len != c->ccid_header.data_len) goto error; - - if (len == c->a->cmd_apdu_head[4]) - /* No Le field*/ - c->a->expected_res_size = 0; - else if (len == (size_t)c->a->cmd_apdu_head[4] + 1) - { - /* it has Le field*/ - c->a->expected_res_size = epo->buf[-1]; - if (c->a->expected_res_size == 0) - c->a->expected_res_size = 256; - len--; - } - else - { - error: - epo->err = 1; - return 0; + //len is the length after lc (whole APDU = len+5) + if (c->a->cmd_apdu_head[4] == 0 && len >= 2) { //extended + len -= 2; + if (len == 0) { + c->a->expected_res_size = (c->a->cmd_apdu_head[5] << 8) | c->a->cmd_apdu_head[6]; + if (c->a->expected_res_size == 0) + c->a->expected_res_size = 0xffff+1; + } + else { + c->a->cmd_apdu_data_len = (c->a->cmd_apdu_data[0] << 8) | c->a->cmd_apdu_data[1]; + len -= 2; + if (len < c->a->cmd_apdu_data_len) + goto error; + c->a->cmd_apdu_data += 2; + if (len == c->a->cmd_apdu_data_len) //no LE + c->a->expected_res_size = 0; + else { + if (len - c->a->cmd_apdu_data_len < 2) + goto error; + c->a->expected_res_size = (c->a->cmd_apdu_data[c->a->cmd_apdu_data_len] << 8) | c->a->cmd_apdu_data[c->a->cmd_apdu_data_len+1]; + if (c->a->expected_res_size == 0) + c->a->expected_res_size = 0xffff+1; + } + } } + else { - c->a->cmd_apdu_data_len += len; + if (len == c->a->cmd_apdu_head[4]) + /* No Le field*/ + c->a->expected_res_size = 0; + else if (len == (size_t)c->a->cmd_apdu_head[4] + 1) + { + /* it has Le field*/ + c->a->expected_res_size = epo->buf[-1]; + if (c->a->expected_res_size == 0) + c->a->expected_res_size = 256; + len--; + } + else + { + error: + DEBUG_INFO("APDU header size error"); + epo->err = 1; + return 0; + } + + c->a->cmd_apdu_data_len += len; + } return 0; } diff --git a/low_flash.c b/low_flash.c index fdbb39e..fa26a2a 100644 --- a/low_flash.c +++ b/low_flash.c @@ -12,14 +12,15 @@ #define TOTAL_FLASH_PAGES 4 -typedef struct PageFlash { +typedef struct page_flash { uint8_t page[FLASH_SECTOR_SIZE]; uintptr_t address; bool ready; bool erase; -} PageFlash_t; + size_t page_size; //this param is for easy erase. It allows to erase with a single call. IT DOES NOT APPLY TO WRITE +} page_flash_t; -static PageFlash_t flash_pages[TOTAL_FLASH_PAGES]; +static page_flash_t flash_pages[TOTAL_FLASH_PAGES]; static mutex_t mtx_flash; @@ -57,7 +58,7 @@ void do_flash() { while (multicore_lockout_start_timeout_us(1000) == false); printf("WRITTING\r\n"); - flash_range_erase(flash_pages[r].address-XIP_BASE, FLASH_SECTOR_SIZE); + flash_range_erase(flash_pages[r].address-XIP_BASE, flash_pages[r].page_size ? ((int)(flash_pages[r].page_size/FLASH_SECTOR_SIZE))*FLASH_SECTOR_SIZE : FLASH_SECTOR_SIZE); while (multicore_lockout_end_timeout_us(1000) == false); flash_pages[r].erase = false; ready_pages--; @@ -76,7 +77,7 @@ void do_flash() void low_flash_init() { mutex_init(&mtx_flash); - memset(flash_pages, 0, sizeof(PageFlash_t)*TOTAL_FLASH_PAGES); + memset(flash_pages, 0, sizeof(page_flash_t)*TOTAL_FLASH_PAGES); } void low_flash_available() @@ -86,18 +87,9 @@ void low_flash_available() mutex_exit(&mtx_flash); } -int -flash_program_halfword (uintptr_t addr, uint16_t data) -{ - off_t offset; +page_flash_t *find_free_page(uintptr_t addr) { uintptr_t addr_alg = addr & -FLASH_SECTOR_SIZE; - PageFlash_t *p = NULL; - if (ready_pages == TOTAL_FLASH_PAGES) { - DEBUG_INFO("ERROR: ALL FLASH PAGES CACHED\r\n"); - return 1; - } - mutex_enter_blocking(&mtx_flash); - + page_flash_t *p = NULL; for (int r = 0; r < TOTAL_FLASH_PAGES; r++) { if ((!flash_pages[r].ready && !flash_pages[r].erase) || flash_pages[r].address == addr_alg) //first available @@ -110,59 +102,66 @@ flash_program_halfword (uintptr_t addr, uint16_t data) p->address = addr_alg; p->ready = true; } - break; + return p; } } + return NULL; +} - if (!p) +int flash_program_block(uintptr_t addr, const uint8_t *data, size_t len) { + uintptr_t addr_alg = addr & -FLASH_SECTOR_SIZE; + page_flash_t *p = NULL; + + mutex_enter_blocking(&mtx_flash); + if (ready_pages == TOTAL_FLASH_PAGES) { + mutex_exit(&mtx_flash); + DEBUG_INFO("ERROR: ALL FLASH PAGES CACHED\r\n"); + return 1; + } + if (!(p = find_free_page(addr))) { DEBUG_INFO("ERROR: FLASH CANNOT FIND A PAGE (rare error)\r\n"); mutex_exit(&mtx_flash); return 1; } - - p->page[addr&(FLASH_SECTOR_SIZE-1)] = (data & 0xff); - p->page[(addr&(FLASH_SECTOR_SIZE-1))+1] = (data >> 8); + memcpy(&p->page[addr&(FLASH_SECTOR_SIZE-1)], data, len); //printf("Flash: modified page %X with data %x %x at [%x-%x] (top page %X)\r\n",addr_alg,(data & 0xff),data>>8,addr&(FLASH_SECTOR_SIZE-1),(addr&(FLASH_SECTOR_SIZE-1))+1,addr); mutex_exit(&mtx_flash); return 0; } -int -flash_erase_page (uintptr_t addr) +int flash_program_halfword (uintptr_t addr, uint16_t data) +{ + return flash_program_block(addr, (const uint8_t *)&data, sizeof(uint16_t)); +} + +int flash_program_word (uintptr_t addr, uint32_t data) +{ + return flash_program_block(addr, (const uint8_t *)&data, sizeof(uint32_t)); +} + +int flash_erase_page (uintptr_t addr, size_t page_size) { - /* uintptr_t addr_alg = addr & -FLASH_SECTOR_SIZE; - PageFlash_t *p = NULL; + page_flash_t *p = NULL; + + mutex_enter_blocking(&mtx_flash); if (ready_pages == TOTAL_FLASH_PAGES) { + mutex_exit(&mtx_flash); DEBUG_INFO("ERROR: ALL FLASH PAGES CACHED\r\n"); return 1; } - mutex_enter_blocking(&mtx_flash); - - for (int r = 0; r < TOTAL_FLASH_PAGES; r++) - { - if ((!flash_pages[r].ready && !flash_pages[r].erase) || flash_pages[r].address == addr_alg) //first available - { - p = &flash_pages[r]; - if (!flash_pages[r].ready && !flash_pages[r].erase) - { - ready_pages++; - p->address = addr_alg; - } - p->erase = true; - break; - } - } - - if (!p) + if (!(p = find_free_page(addr))) { DEBUG_INFO("ERROR: FLASH CANNOT FIND A PAGE (rare error)\r\n"); mutex_exit(&mtx_flash); return 1; } + p->erase = true; + p->ready = false; + p->page_size = page_size; mutex_exit(&mtx_flash); - */ + return 0; } @@ -177,14 +176,3 @@ flash_check_blank (const uint8_t *p_start, size_t size) return 1; } - -int -flash_write (uintptr_t dst_addr, const uint8_t *src, size_t len) -{ - size_t len_alg = (len + (FLASH_SECTOR_SIZE - 1)) & -FLASH_SECTOR_SIZE; - uintptr_t add_alg = dst_addr & -FLASH_SECTOR_SIZE; - printf("WRITE ATTEMPT %X (%d) %X (%d)\r\n",dst_addr,len,add_alg,len_alg); - uint32_t ints = save_and_disable_interrupts(); - flash_range_program(add_alg-XIP_BASE, src, len_alg); - restore_interrupts (ints); -} diff --git a/openpgp.c b/openpgp.c index 8f9c5e5..910abc1 100644 --- a/openpgp.c +++ b/openpgp.c @@ -35,17 +35,14 @@ #include "random.h" #include "pico/util/queue.h" #include "pico/multicore.h" +#include "hsm2040.h" +#include "tusb.h" static queue_t *openpgp_comm; #define USER_PASSWD_MINLEN 6 #define ADMIN_PASSWD_MINLEN 8 -#define CLS(a) a.cmd_apdu_head[0] -#define INS(a) a.cmd_apdu_head[1] -#define P1(a) a.cmd_apdu_head[2] -#define P2(a) a.cmd_apdu_head[3] - #define INS_VERIFY 0x20 #define INS_CHANGE_REFERENCE_DATA 0x24 #define INS_PSO 0x2a @@ -57,6 +54,7 @@ static queue_t *openpgp_comm; #define INS_INTERNAL_AUTHENTICATE 0x88 #define INS_SELECT_FILE 0xa4 #define INS_READ_BINARY 0xb0 +#define INS_READ_BINARY_ODD 0xb1 #define INS_GET_DATA 0xca #define INS_WRITE_BINARY 0xd0 #define INS_UPDATE_BINARY 0xd6 @@ -84,10 +82,10 @@ select_file_TOP_result[] __attribute__ ((aligned (1))) = { 0x00, 0x00 /* PIN status: OK, PIN blocked?: No */ }; -void -set_res_sw (uint8_t sw1, uint8_t sw2) +uint16_t set_res_sw (uint8_t sw1, uint8_t sw2) { - apdu.sw = (sw1 << 8) | sw2; + apdu.sw = (sw1 << 8) | sw2; + return make_uint16_t(sw1, sw2); } #define FILE_NONE 0 @@ -100,32 +98,221 @@ set_res_sw (uint8_t sw1, uint8_t sw2) #define FILE_EF_UPDATE_KEY_2 7 #define FILE_EF_UPDATE_KEY_3 8 #define FILE_EF_CH_CERTIFICATE 9 +#define FILE_DF_SC_HSM 10 #define FILE_CARD_TERMINATED 255 -uint8_t file_selection; +#define FILE_TYPE_UNKNOWN 0x00 +#define FILE_TYPE_DF 0x04 +#define FILE_TYPE_INTERNAL_EF 0x03 +#define FILE_TYPE_WORKING_EF 0x01 +#define FILE_TYPE_BSO 0x10 -static void -gpg_init (void) +/* EF structures */ +#define FILE_EF_UNKNOWN 0x00 +#define FILE_EF_TRANSPARENT 0x01 +#define FILE_EF_LINEAR_FIXED 0x02 +#define FILE_EF_LINEAR_FIXED_TLV 0x03 +#define FILE_EF_LINEAR_VARIABLE 0x04 +#define FILE_EF_LINEAR_VARIABLE_TLV 0x05 +#define FILE_EF_CYCLIC 0x06 +#define FILE_EF_CYCLIC_TLV 0x07 + +#define ACL_OP_DELETE_SELF 0x00 +#define ACL_OP_CREATE_DF 0x01 +#define ACL_OP_CREATE_EF 0x02 +#define ACL_OP_DELETE_CHILD 0x03 +#define ACL_OP_WRITE 0x04 +#define ACL_OP_UPDATE_ERASE 0x05 +#define ACL_OP_READ_SEARCH 0x06 + +typedef struct pkcs15_entry { - const uint8_t *flash_do_start; - const uint8_t *flash_do_end; + const uint16_t fid; + const uint8_t parent; //entry number in the whole table!! + const uint8_t *name; + const uint8_t type; + const uint8_t *data; //should include 2 bytes len at begining + const uint8_t ef_structure; + const uint8_t acl[7]; +} pkcs15_entry_t; - flash_do_storage_init (&flash_do_start, &flash_do_end); - - if (flash_do_start == NULL) - file_selection = FILE_CARD_TERMINATED; - else - file_selection = FILE_NONE; - - gpg_data_scan (flash_do_start, flash_do_end); - flash_key_storage_init (); - multicore_lockout_victim_init(); +//puts FCI in the RAPDU +void process_fci(const pkcs15_entry_t *pe) { + uint8_t *p = res_APDU; + uint8_t buf[64]; + res_APDU_size = 0; + res_APDU[res_APDU_size++] = 0x6f; + res_APDU[res_APDU_size++] = 0x00; //computed later + + res_APDU[res_APDU_size++] = 0x81; + res_APDU[res_APDU_size++] = 2; + if (pe->data) + memcpy(res_APDU+res_APDU_size, pe->data, 2); + else + memset(res_APDU+res_APDU_size, 0, 2); + res_APDU_size += 2; + + res_APDU[res_APDU_size++] = 0x82; + res_APDU[res_APDU_size++] = 1; + res_APDU[res_APDU_size] = 0; + if (pe->type == FILE_TYPE_INTERNAL_EF) + res_APDU[res_APDU_size++] |= 0x08; + else if (pe->type == FILE_TYPE_WORKING_EF) + res_APDU[res_APDU_size++] |= pe->ef_structure & 0x7; + else if (pe->type == FILE_TYPE_DF) + res_APDU[res_APDU_size++] |= 0x38; + + res_APDU[res_APDU_size++] = 0x83; + res_APDU[res_APDU_size++] = 2; + put_uint16_t(pe->fid, res_APDU+res_APDU_size); + res_APDU_size += 2; + res_APDU[1] = res_APDU_size-2; } -static void -gpg_fini (void) +const uint8_t t[] = { + 0x01,0xbb, + 0x7F,0x21,0x82,0x01,0xB6,0x7F,0x4E,0x82,0x01,0x6E,0x5F,0x29,0x01,0x00,0x42,0x0E,0x44,0x45,0x43,0x56,0x43,0x41,0x65,0x49,0x44,0x30,0x30,0x31,0x30,0x32,0x7F,0x49,0x82,0x01,0x1D,0x06,0x0A,0x04,0x00,0x7F,0x00,0x07,0x02,0x02,0x02,0x02,0x03,0x81,0x20,0xA9,0xFB,0x57,0xDB,0xA1,0xEE,0xA9,0xBC,0x3E,0x66,0x0A,0x90,0x9D,0x83,0x8D,0x72,0x6E,0x3B,0xF6,0x23,0xD5,0x26,0x20,0x28,0x20,0x13,0x48,0x1D,0x1F,0x6E,0x53,0x77,0x82,0x20,0x7D,0x5A,0x09,0x75,0xFC,0x2C,0x30,0x57,0xEE,0xF6,0x75,0x30,0x41,0x7A,0xFF,0xE7,0xFB,0x80,0x55,0xC1,0x26,0xDC,0x5C,0x6C,0xE9,0x4A,0x4B,0x44,0xF3,0x30,0xB5,0xD9,0x83,0x20,0x26,0xDC,0x5C,0x6C,0xE9,0x4A,0x4B,0x44,0xF3,0x30,0xB5,0xD9,0xBB,0xD7,0x7C,0xBF,0x95,0x84,0x16,0x29,0x5C,0xF7,0xE1,0xCE,0x6B,0xCC,0xDC,0x18,0xFF,0x8C,0x07,0xB6,0x84,0x41,0x04,0x8B,0xD2,0xAE,0xB9,0xCB,0x7E,0x57,0xCB,0x2C,0x4B,0x48,0x2F,0xFC,0x81,0xB7,0xAF,0xB9,0xDE,0x27,0xE1,0xE3,0xBD,0x23,0xC2,0x3A,0x44,0x53,0xBD,0x9A,0xCE,0x32,0x62,0x54,0x7E,0xF8,0x35,0xC3,0xDA,0xC4,0xFD,0x97,0xF8,0x46,0x1A,0x14,0x61,0x1D,0xC9,0xC2,0x77,0x45,0x13,0x2D,0xED,0x8E,0x54,0x5C,0x1D,0x54,0xC7,0x2F,0x04,0x69,0x97,0x85,0x20,0xA9,0xFB,0x57,0xDB,0xA1,0xEE,0xA9,0xBC,0x3E,0x66,0x0A,0x90,0x9D,0x83,0x8D,0x71,0x8C,0x39,0x7A,0xA3,0xB5,0x61,0xA6,0xF7,0x90,0x1E,0x0E,0x82,0x97,0x48,0x56,0xA7,0x86,0x41,0x04,0x33,0x47,0xEC,0xF9,0x6F,0xFB,0x4B,0xD9,0xB8,0x55,0x4E,0xFB,0xCC,0xFC,0x7D,0x0B,0x24,0x2F,0x10,0x71,0xE2,0x9B,0x4C,0x9C,0x62,0x2C,0x79,0xE3,0x39,0xD8,0x40,0xAF,0x67,0xBE,0xB9,0xB9,0x12,0x69,0x22,0x65,0xD9,0xC1,0x6C,0x62,0x57,0x3F,0x45,0x79,0xFF,0xD4,0xDE,0x2D,0xE9,0x2B,0xAB,0x40,0x9D,0xD5,0xC5,0xD4,0x82,0x44,0xA9,0xF7,0x87,0x01,0x01,0x5F,0x20,0x0E,0x44,0x45,0x43,0x56,0x43,0x41,0x65,0x49,0x44,0x30,0x30,0x31,0x30,0x32,0x7F,0x4C,0x12,0x06,0x09,0x04,0x00,0x7F,0x00,0x07,0x03,0x01,0x02,0x02,0x53,0x05,0xFE,0x0F,0x01,0xFF,0xFF,0x5F,0x25,0x06,0x01,0x00,0x01,0x00,0x01,0x08,0x5F,0x24,0x06,0x01,0x03,0x01,0x00,0x01,0x08,0x5F,0x37,0x40,0x50,0x67,0x14,0x5C,0x68,0xCA,0xE9,0x52,0x0F,0x5B,0xB3,0x48,0x17,0xF1,0xCA,0x9C,0x43,0x59,0x3D,0xB5,0x64,0x06,0xC6,0xA3,0xB0,0x06,0xCB,0xF3,0xF3,0x14,0xE7,0x34,0x9A,0xCF,0x0C,0xC6,0xBF,0xEB,0xCB,0xDE,0xFD,0x10,0xB4,0xDC,0xF0,0xF2,0x31,0xDA,0x56,0x97,0x7D,0x88,0xF9,0xF9,0x01,0x82,0xD1,0x99,0x07,0x6A,0x56,0x50,0x64,0x51 + }; +const uint8_t token_info[] = { + 0x0, 0x1f, + 0x30, 0x1d, 0x2, 0x1, 0x2, 0x4, 0x4, 0x6, 0x0, 0x0, 0x0, 0xc, 0x6, 0x4d, 0x61, 0x6e, 0x75, 0x49, 0x44, 0x80, 0x6, 0x50, 0x61, 0x74, 0x61, 0x74, 0x61, 0x3, 0x2, 0x7, 0x80 +}; + +const pkcs15_entry_t pkcs15_entries[] = { + { .fid = 0x3f00, .parent = 0xff, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = {0} }, // MF + { .fid = 0x2f00, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.DIR + { .fid = 0x2f01, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.ATR + { .fid = 0x2f02, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF,.data = t, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.GDO + { .fid = 0x2f03, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF,.data = token_info, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.TokenInfo + { .fid = 0x5015, .parent = 0, .name = NULL, .type = FILE_TYPE_DF, .data = NULL, .ef_structure = 0, .acl = {0} }, //DF.PKCS15 + { .fid = 0x5031, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.ODF + { .fid = 0x5032, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.TokenInfo + { .fid = 0x5033, .parent = 0, .name = NULL, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, //EF.UnusedSpace + { .fid = 0x0000, .parent = 0, .name = openpgpcard_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, + { .fid = 0x0000, .parent = 0, .name = sc_hsm_aid, .type = FILE_TYPE_WORKING_EF, .data = NULL, .ef_structure = FILE_EF_TRANSPARENT, .acl = {0} }, + { .fid = 0x0000, .parent = 0xff, .name = NULL, .type = FILE_TYPE_UNKNOWN, .data = NULL, .ef_structure = 0, .acl = {0} } //end +}; + +const pkcs15_entry_t *MF = &pkcs15_entries[0]; +const pkcs15_entry_t *pkcs15_last = &pkcs15_entries[sizeof(pkcs15_entries)/sizeof(pkcs15_entry_t)-1]; +const pkcs15_entry_t *pkcs15_openpgp = &pkcs15_entries[sizeof(pkcs15_entries)/sizeof(pkcs15_entry_t)-3]; +const pkcs15_entry_t *pkcs15_sc_hsm = &pkcs15_entries[sizeof(pkcs15_entries)/sizeof(pkcs15_entry_t)-2]; + +extern const pkcs15_entry_t *search_by_fid(const uint16_t, const pkcs15_entry_t *, const uint8_t); + +bool card_terminated = false; + +#define SPECIFY_EF 0x1 +#define SPECIFY_DF 0x2 +#define SPECIFY_ANY 0x3 + +#define MAX_DEPTH 4 + +bool is_parent(const pkcs15_entry_t *child, const pkcs15_entry_t *parent) { + if (child == parent) + return true; + if (child == MF) + return false; + return is_parent(&pkcs15_entries[child->parent], parent); +} + +const pkcs15_entry_t *search_by_name(uint8_t *name, uint16_t namelen) { + for (const pkcs15_entry_t *p = pkcs15_entries; p != pkcs15_last; p++) { + if (p->name && *p->name == apdu.cmd_apdu_data_len && memcmp(p->name+1, name, namelen) == 0) { + return p; + } + } + return NULL; +} + +const pkcs15_entry_t *search_by_fid(const uint16_t fid, const pkcs15_entry_t *parent, const uint8_t sp) { + + for (const pkcs15_entry_t *p = pkcs15_entries; p != pkcs15_last; p++) { + if (p->fid != 0x0000 && p->fid == fid) { + if (!parent || (parent && is_parent(p, parent))) { + if (!sp || sp == SPECIFY_ANY || (((sp & SPECIFY_EF) && (p->type & FILE_TYPE_INTERNAL_EF)) || ((sp & SPECIFY_DF) && p->type == FILE_TYPE_DF))) + return p; + } + } + } + return NULL; +} + +uint8_t make_path_buf(const pkcs15_entry_t *pe, uint8_t *buf, uint8_t buflen, const pkcs15_entry_t *top) { + if (!buflen) + return 0; + if (pe == top) //MF or relative DF + return 0; + put_uint16_t(pe->fid, buf); + return make_path_buf(&pkcs15_entries[pe->parent], buf+2, buflen-2, top)+2; +} + +uint8_t make_path(const pkcs15_entry_t *pe, const pkcs15_entry_t *top, uint8_t *path) { + uint8_t buf[MAX_DEPTH*2], *p = path; + put_uint16_t(pe->fid, buf); + uint8_t depth = make_path_buf(&pkcs15_entries[pe->parent], buf+2, sizeof(buf)-2, top)+2; + for (int d = depth-2; d >= 0; d -= 2) { + memcpy(p, buf+d, 2); + p += 2; + } + return depth; +} + +const pkcs15_entry_t *search_by_path(const uint8_t *pe_path, uint8_t pathlen, const pkcs15_entry_t *parent) { + uint8_t path[MAX_DEPTH*2]; + if (pathlen > sizeof(path)) { + return NULL; + } + for (const pkcs15_entry_t *p = pkcs15_entries; p != pkcs15_last; p++) { + uint8_t depth = make_path(p, parent, path); + if (pathlen == depth && memcmp(path, pe_path, depth)) + return p; + } + return NULL; +} + +uint8_t file_selection; +const pkcs15_entry_t *currentEF = NULL; +const pkcs15_entry_t *currentDF = NULL; +const pkcs15_entry_t *selected_applet = NULL; +bool isUserAuthenticated = false; + +bool authenticate_action(const pkcs15_entry_t *ef, uint8_t op) { + uint8_t acl = ef->acl[op]; + if (acl == 0x0) + return true; + else if (acl == 0xff) + return false; + else if (acl == 0x90 || acl & 0x9F == 0x10) { + // PIN required. + if(isUserAuthenticated) { + return true; + } + else { + return false; + } + } + return false; +} + +static void gpg_init (void) { - ac_fini (); + const uint8_t *flash_do_start; + const uint8_t *flash_do_end; + + flash_do_storage_init (&flash_do_start, &flash_do_end); + + if (flash_do_start == NULL) + card_terminated = true; + + gpg_data_scan (flash_do_start, flash_do_end); + flash_key_storage_init (); + multicore_lockout_victim_init(); +} + +static void gpg_fini (void) +{ + ac_fini (); } #if defined(PINPAD_SUPPORT) @@ -146,86 +333,90 @@ get_pinpad_input (int msg_code) } #endif -static void -cmd_verify (queue_t *ccid_comm) +static int cmd_verify (queue_t *ccid_comm) { - int len; - uint8_t p1 = P1 (apdu); - uint8_t p2 = P2 (apdu); - int r; - const uint8_t *pw; - - (void)ccid_comm; - DEBUG_INFO (" - VERIFY\r\n"); - DEBUG_BYTE (p2); - - len = apdu.cmd_apdu_data_len; - pw = apdu.cmd_apdu_data; - - if (len == 0) - { - if (p1 == 0) - { /* This is to examine status. */ - if (p2 == 0x81) - r = ac_check_status (AC_PSO_CDS_AUTHORIZED); - else if (p2 == 0x82) - r = ac_check_status (AC_OTHER_AUTHORIZED); - else - r = ac_check_status (AC_ADMIN_AUTHORIZED); - - if (r) - /* If authentication done already, return success. */ - GPG_SUCCESS (); - else - { /* If not, return retry counter, encoded. */ - r = gpg_pw_get_retry_counter (p2); - set_res_sw (0x63, 0xc0 | (r&0x0f)); - } - } - else if (p1 == 0xff) - { /* Reset the status. */ - if (p2 == 0x81) - ac_reset_pso_cds (); - else if (p2 == 0x82) - ac_reset_other (); - else - ac_reset_admin (); - GPG_SUCCESS (); - } - else - GPG_BAD_P1_P2 (); - return; + int len; + uint8_t p1 = P1 (apdu); + uint8_t p2 = P2 (apdu); + int r; + const uint8_t *pw; + + (void)ccid_comm; + DEBUG_INFO (" - VERIFY\r\n"); + DEBUG_BYTE (p2); + + len = apdu.cmd_apdu_data_len; + pw = apdu.cmd_apdu_data; + printf("%X %X %X\r\n",selected_applet,pkcs15_openpgp,pkcs15_sc_hsm); + + if (selected_applet == pkcs15_openpgp) { + if (len == 0) + { + if (p1 == 0) + { /* This is to examine status. */ + if (p2 == 0x81) + r = ac_check_status (AC_PSO_CDS_AUTHORIZED); + else if (p2 == 0x82) + r = ac_check_status (AC_OTHER_AUTHORIZED); + else + r = ac_check_status (AC_ADMIN_AUTHORIZED); + + if (r) + /* If authentication done already, return success. */ + return GPG_SUCCESS (); + else + { /* If not, return retry counter, encoded. */ + r = gpg_pw_get_retry_counter (p2); + set_res_sw (0x63, 0xc0 | (r&0x0f)); + } + } + else if (p1 == 0xff) + { /* Reset the status. */ + if (p2 == 0x81) + ac_reset_pso_cds (); + else if (p2 == 0x82) + ac_reset_other (); + else + ac_reset_admin (); + return GPG_SUCCESS (); + } + else + return GPG_BAD_P1_P2 (); + } + + if (gpg_do_kdf_check (len, 1) == 0) + { + return GPG_CONDITION_NOT_SATISFIED (); + } + + /* This is real authentication. */ + if (p2 == 0x81) + r = verify_pso_cds (pw, len); + else if (p2 == 0x82) + r = verify_other (pw, len); + else + r = verify_admin (pw, len); + + if (r < 0) + { + DEBUG_INFO ("failed\r\n"); + return GPG_SECURITY_FAILURE (); + } + else if (r == 0) + { + DEBUG_INFO ("blocked\r\n"); + return GPG_SECURITY_AUTH_BLOCKED (); + } + else + { + DEBUG_INFO ("good\r\n"); + return GPG_SUCCESS (); + } } - - if (gpg_do_kdf_check (len, 1) == 0) - { - GPG_CONDITION_NOT_SATISFIED (); - return; - } - - /* This is real authentication. */ - if (p2 == 0x81) - r = verify_pso_cds (pw, len); - else if (p2 == 0x82) - r = verify_other (pw, len); - else - r = verify_admin (pw, len); - - if (r < 0) - { - DEBUG_INFO ("failed\r\n"); - GPG_SECURITY_FAILURE (); - } - else if (r == 0) - { - DEBUG_INFO ("blocked\r\n"); - GPG_SECURITY_AUTH_BLOCKED (); - } - else - { - DEBUG_INFO ("good\r\n"); - GPG_SUCCESS (); + else if (selected_applet == pkcs15_sc_hsm) { + return GPG_BAD_P1_P2(); } + return 0; } int @@ -768,152 +959,149 @@ gpg_get_firmware_update_key (uint8_t keyno) #define FILEID_CH_CERTIFICATE_IS_VALID 0 #endif -static void -cmd_read_binary (queue_t *ccid_comm) +static int cmd_read_binary (queue_t *ccid_comm) { - int is_short_EF = (P1 (apdu) & 0x80) != 0; - uint8_t file_id; - uint16_t offset; - - (void)ccid_comm; - DEBUG_INFO (" - Read binary\r\n"); - - if (is_short_EF) - file_id = (P1 (apdu) & 0x1f); - else - file_id = file_selection - FILE_EF_SERIAL_NO + FILEID_SERIAL_NO; - - if (is_short_EF) + uint16_t fid; + uint32_t offset; + uint8_t ins = INS(apdu), p1 = P1(apdu), p2 = P2(apdu); + const pkcs15_entry_t *ef = NULL; + + (void)ccid_comm; + DEBUG_INFO (" - Read binary\r\n"); + + if ((ins & 0x1) == 0) { - file_selection = file_id - FILEID_SERIAL_NO + FILE_EF_SERIAL_NO; - offset = P2 (apdu); + 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))) + return SW_FILE_NOT_FOUND (); + + if (apdu.cmd_apdu_data[0] != 0x54) + return SW_WRONG_DATA(); + + offset = 0; + for (int d = 0; d < apdu.cmd_apdu_data[1]; d++) + offset |= apdu.cmd_apdu_data[2+d]<<(apdu.cmd_apdu_data[1]-1-d)*8; + } + + } + + if (!authenticate_action(ef, ACL_OP_READ_SEARCH)) { + return SW_SECURITY_STATUS_NOT_SATISFIED(); + } + if (ef->data) { + uint16_t data_len = get_uint16_t(ef->data, 0); + if (offset > data_len) + return SW_WRONG_P1P2(); + + uint16_t maxle = data_len-offset; + if (apdu.expected_res_size > maxle) + apdu.expected_res_size = maxle; + res_APDU = ef->data+2+offset; + res_APDU_size = data_len-offset; } - else - offset = (P1 (apdu) << 8) | P2 (apdu); - if (file_id == FILEID_SERIAL_NO) - { - if (offset != 0) - GPG_BAD_P1_P2 (); - else - { - gpg_do_get_data (0x004f, 1); /* Get AID... */ - res_APDU[0] = 0x5a; /* ... and overwrite the first byte of data. */ - } - return; - } -#ifdef FLASH_UPGRADE_SUPPORT - else if (file_id >= FILEID_UPDATE_KEY_0 && file_id <= FILEID_UPDATE_KEY_3) - { - if (offset != 0) - GPG_MEMORY_FAILURE (); - else - { - const uint8_t *p; - - p = gpg_get_firmware_update_key (file_id - FILEID_UPDATE_KEY_0); - res_APDU_size = FIRMWARE_UPDATE_KEY_CONTENT_LEN; - memcpy (res_APDU, p, FIRMWARE_UPDATE_KEY_CONTENT_LEN); - GPG_SUCCESS (); - } - } -#endif -#if defined(CERTDO_SUPPORT) - else if (file_id == FILEID_CH_CERTIFICATE) - { - const uint8_t *p; - uint16_t len = 256; - - p = ch_certificate_start; - if (offset >= FLASH_CH_CERTIFICATE_SIZE) - GPG_MEMORY_FAILURE (); - else - { - if (offset + len >= FLASH_CH_CERTIFICATE_SIZE) - len = FLASH_CH_CERTIFICATE_SIZE - offset; - - res_APDU_size = len; - memcpy (res_APDU, p + offset, len); - GPG_SUCCESS (); - } - } -#endif - else - { - GPG_NO_FILE (); - return; - } + return GPG_SUCCESS (); } - -static void -cmd_select_file (queue_t *ccid_comm) +void select_file(const pkcs15_entry_t *pe) { + if (!pe) + { + currentDF = MF; + currentEF = NULL; + } + else if (pe->type & FILE_TYPE_INTERNAL_EF) { + currentEF = pe; + currentDF = &pkcs15_entries[pe->parent]; + } + else { + currentDF = pe; + } + if (currentEF == pkcs15_openpgp || currentEF == pkcs15_sc_hsm) + selected_applet = currentEF; +} +static uint16_t cmd_select_file (queue_t *ccid_comm) { - (void)ccid_comm; - if (P1 (apdu) == 4) /* Selection by DF name */ - { - DEBUG_INFO (" - select DF by name\r\n"); - - /* name = D2 76 00 01 24 01 */ - if (apdu.cmd_apdu_data_len != 6 - || memcmp (openpgpcard_aid, apdu.cmd_apdu_data, 6) != 0) - { - DEBUG_SHORT (apdu.cmd_apdu_data_len); - DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len); - - GPG_NO_FILE (); - return; - } - - if (file_selection == FILE_CARD_TERMINATED) - { - GPG_APPLICATION_TERMINATED (); - return; - } - - file_selection = FILE_DF_OPENPGP; - - /* Behave just like original OpenPGP card. */ - GPG_SUCCESS (); + (void)ccid_comm; + uint8_t p1 = P1(apdu); + uint8_t p2 = P2(apdu); + const pkcs15_entry_t *pe = NULL; + uint16_t fid = 0x0; + + // Only "first or only occurence" supported + //if ((p2 & 0xF3) != 0x00) { + // return SW_INCORRECT_P1P2(); + //} + + if (apdu.cmd_apdu_data_len >= 2) + fid = get_uint16_t(apdu.cmd_apdu_data, 0); + + if (p1 == 0x0) { //Select MF, DF or EF - File identifier or absent + if (apdu.cmd_apdu_data_len == 0) { + pe = MF; + ac_fini(); + } + else if (apdu.cmd_apdu_data_len == 2) { + if (!(pe = search_by_fid(fid, NULL, SPECIFY_ANY))) { + return GPG_NO_FILE (); + } + } } - else if (apdu.cmd_apdu_data_len == 2 - && apdu.cmd_apdu_data[0] == 0x2f && apdu.cmd_apdu_data[1] == 0x02) - { - DEBUG_INFO (" - select 0x2f02 EF\r\n"); - /* - * MF.EF-GDO -- Serial number of the card and name of the owner - */ - GPG_SUCCESS (); - file_selection = FILE_EF_SERIAL_NO; + else if (p1 == 0x01) { //Select child DF - DF identifier + if (!(pe = search_by_fid(fid, currentDF, SPECIFY_DF))) { + return GPG_NO_FILE (); + } } - else if (apdu.cmd_apdu_data_len == 2 - && apdu.cmd_apdu_data[0] == 0x3f && apdu.cmd_apdu_data[1] == 0x00) - { - DEBUG_INFO (" - select ROOT MF\r\n"); - if (P2 (apdu) == 0x0c) - { - GPG_SUCCESS (); - } - else - { - int len = sizeof (select_file_TOP_result); - - res_APDU_size = len; - memcpy (res_APDU, select_file_TOP_result, len); - res_APDU[2] = (data_objects_number_of_bytes & 0xff); - res_APDU[3] = (data_objects_number_of_bytes >> 8); - GPG_SUCCESS (); - } - - file_selection = FILE_MF; - ac_fini (); /* Reset authentication */ + else if (p1 == 0x02) { //Select EF under the current DF - EF identifier + if (!(pe = search_by_fid(fid, currentDF, SPECIFY_EF))) { + return GPG_NO_FILE (); + } } - else - { - DEBUG_INFO (" - select ?? \r\n"); - - file_selection = FILE_NONE; - GPG_NO_FILE (); + else if (p1 == 0x03) { //Select parent DF of the current DF - Absent + if (apdu.cmd_apdu_data_len != 0) + return GPG_NO_FILE (); } + else if (p1 == 0x04) { //Select by DF name - e.g., [truncated] application identifier + if (!(pe = search_by_name(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len))) { + return GPG_NO_FILE (); + } + if (card_terminated) { + return GPG_APPLICATION_TERMINATED (); + } + } + else if (p1 == 0x08) { //Select from the MF - Path without the MF identifier + if (!(pe = search_by_path(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, MF))) { + return GPG_NO_FILE (); + } + } + else if (p1 == 0x09) { //Select from the current DF - Path without the current DF identifier + if (!(pe = search_by_path(apdu.cmd_apdu_data, apdu.cmd_apdu_data_len, currentDF))) { + return GPG_NO_FILE (); + } + } + if ((p2 & 0xfc) == 0x00 || (p2 & 0xfc) == 0x04) { + process_fci(pe); + } + else + return SW_INCORRECT_P1P2(); + select_file(pe); + return GPG_SUCCESS (); } static void @@ -1471,7 +1659,8 @@ cmd_activate_file (queue_t *ccid_comm) } flash_activate (); - file_selection = FILE_DF_OPENPGP; + //file_selection = FILE_DF_OPENPGP; + file_selection = FILE_DF_SC_HSM; GPG_SUCCESS (); } @@ -1483,7 +1672,8 @@ cmd_terminate_df (queue_t *ccid_comm) uint8_t p2 = P2 (apdu); (void)ccid_comm; - if (file_selection != FILE_DF_OPENPGP) + //if (file_selection != FILE_DF_OPENPGP) + if (file_selection != FILE_DF_SC_HSM) { GPG_NO_RECORD (); return; @@ -1546,6 +1736,7 @@ const struct command cmds[] = { { INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate }, { INS_SELECT_FILE, cmd_select_file }, { INS_READ_BINARY, cmd_read_binary }, /* Not in OpenPGP card protocol */ + { INS_READ_BINARY_ODD, cmd_read_binary }, /* Not in OpenPGP card protocol */ { INS_GET_DATA, cmd_get_data }, { INS_WRITE_BINARY, cmd_write_binary}, /* Not in OpenPGP card protocol */ #if defined(CERTDO_SUPPORT) @@ -1562,37 +1753,35 @@ const struct command cmds[] = { static void process_command_apdu (queue_t *ccid_comm) { - int i; - uint8_t cmd = INS (apdu); + int i; + uint8_t cmd = INS (apdu); - for (i = 0; i < NUM_CMDS; i++) - if (cmds[i].command == cmd) - break; + for (i = 0; i < NUM_CMDS; i++) + if (cmds[i].command == cmd) + break; - if (i < NUM_CMDS) + if (i < NUM_CMDS) { - if (file_selection == FILE_CARD_TERMINATED - && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE - && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE) - GPG_APPLICATION_TERMINATED (); - else if (file_selection != FILE_DF_OPENPGP - && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE - && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE - && cmd != INS_WRITE_BINARY && cmd != INS_UPDATE_BINARY - && cmd != INS_READ_BINARY) - GPG_NO_RECORD (); - else - { - //chopstx_setcancelstate (1); - cmds[i].cmd_handler (ccid_comm); - //chopstx_setcancelstate (0); - } + if (file_selection == FILE_CARD_TERMINATED + && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE + && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE) + GPG_APPLICATION_TERMINATED (); + else if (selected_applet == NULL + && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE + && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE + && cmd != INS_WRITE_BINARY && cmd != INS_UPDATE_BINARY + && cmd != INS_READ_BINARY && cmd != INS_READ_BINARY_ODD) + GPG_NO_RECORD (); + else + { + cmds[i].cmd_handler (ccid_comm); + } } - else + else { - DEBUG_INFO (" - ??"); - DEBUG_BYTE (cmd); - GPG_NO_INS (); + DEBUG_INFO (" - ??"); + DEBUG_BYTE (cmd); + GPG_NO_INS (); } } diff --git a/status-code.h b/status-code.h index 537d56c..9943279 100644 --- a/status-code.h +++ b/status-code.h @@ -1,14 +1,36 @@ -#define GPG_APPLICATION_TERMINATED() set_res_sw (0x62, 0x85) -#define GPG_MEMORY_FAILURE() set_res_sw (0x65, 0x81) -#define GPG_WRONG_LENGTH() set_res_sw (0x67, 0x00) -#define GPG_SECURITY_FAILURE() set_res_sw (0x69, 0x82) -#define GPG_SECURITY_AUTH_BLOCKED() set_res_sw (0x69, 0x83) -#define GPG_CONDITION_NOT_SATISFIED() set_res_sw (0x69, 0x85) -#define GPG_COMMAND_NOT_ALLOWED() set_res_sw (0x69, 0x86) -#define GPG_FUNCTION_NOT_SUPPORTED() set_res_sw (0x6a, 0x81) -#define GPG_NO_FILE() set_res_sw (0x6a, 0x82) -#define GPG_NO_RECORD() set_res_sw (0x6a, 0x88) -#define GPG_BAD_P1_P2() set_res_sw (0x6b, 0x00) -#define GPG_NO_INS() set_res_sw (0x6d, 0x00) -#define GPG_ERROR() set_res_sw (0x6f, 0x00) -#define GPG_SUCCESS() set_res_sw (0x90, 0x00) +#define SW_BYTES_REMAINING_00() set_res_sw (0x61, 0x00) +#define SW_WARNING_STATE_UNCHANGED() set_res_sw (0x62, 0x00) +#define GPG_APPLICATION_TERMINATED() set_res_sw (0x62, 0x85) +#define GPG_MEMORY_FAILURE() set_res_sw (0x65, 0x81) +#define GPG_WRONG_LENGTH() set_res_sw (0x67, 0x00) +#define SW_WRONG_LENGTH() set_res_sw (0x67, 0x00) +#define SW_WRONG_DATA() set_res_sw (0x67, 0x00) +#define SW_LOGICAL_CHANNEL_NOT_SUPPORTED() set_res_sw (0x68, 0x81) +#define SW_SECURE_MESSAGING_NOT_SUPPORTED() set_res_sw (0x68, 0x82) +#define GPG_SECURITY_FAILURE() set_res_sw (0x69, 0x82) +#define SW_SECURITY_STATUS_NOT_SATISFIED() set_res_sw (0x69, 0x82) +#define GPG_SECURITY_AUTH_BLOCKED() set_res_sw (0x69, 0x83) +#define SW_FILE_INVALID () set_res_sw (0x69, 0x83) +#define SW_DATA_INVALID () set_res_sw (0x69, 0x84) +#define GPG_CONDITION_NOT_SATISFIED() set_res_sw (0x69, 0x85) +#define SW_CONDITIONS_NOT_SATISFIED set_res_sw (0x69, 0x85) +#define GPG_COMMAND_NOT_ALLOWED() set_res_sw (0x69, 0x86) +#define SW_COMMAND_NOT_ALLOWED() set_res_sw (0x69, 0x86) +#define SW_APPLET_SELECT_FAILED() set_res_sw (0x69, 0x99) +#define GPG_FUNCTION_NOT_SUPPORTED() set_res_sw (0x6a, 0x81) +#define SW_FUNC_NOT_SUPPORTED() set_res_sw (0x6A, 0x81) +#define GPG_NO_FILE() set_res_sw (0x6a, 0x82) +#define SW_FILE_NOT_FOUND() set_res_sw (0x6A, 0x82) +#define SW_RECORD_NOT_FOUND() set_res_sw (0x6A, 0x83) +#define SW_FILE_FULL() set_res_sw (0x6A, 0x84) +#define SW_INCORRECT_P1P2() set_res_sw (0x6A, 0x86) +#define GPG_NO_RECORD() set_res_sw (0x6a, 0x88) +#define GPG_BAD_P1_P2() set_res_sw (0x6b, 0x00) +#define SW_WRONG_P1P2() set_res_sw (0x6B, 0x00) +#define SW_CORRECT_LENGTH_00() set_res_sw (0x6C, 0x00) +#define GPG_NO_INS() set_res_sw (0x6d, 0x00) +#define SW_INS_NOT_SUPPORTED() set_res_sw (0x6D, 0x00) +#define SW_CLA_NOT_SUPPORTED() set_res_sw (0x6E, 0x00) +#define GPG_ERROR() set_res_sw (0x6f, 0x00) +#define SW_UNKNOWN() set_res_sw (0x6F, 0x00) +#define GPG_SUCCESS() set_res_sw (0x90, 0x00)