diff --git a/Makefile b/Makefile index 8391c06..9620017 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS = -std=c99 +CFLAGS = -std=c11 -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-empty-translation-unit SRCS = OBJS = BIN = nonsenselock diff --git a/src/smartcard/apdu.c b/src/smartcard/apdu.c new file mode 100644 index 0000000..74eb76b --- /dev/null +++ b/src/smartcard/apdu.c @@ -0,0 +1,142 @@ +#include +#include "apdu.h" +#include "error.h" + + +static inline void c_apdu_init(struct nsl_smartcard_c_apdu *apdu) +{ + apdu->len = apdu->outlen = 0; + apdu->data = NULL; +} + +static inline int c_apdu_read(struct nsl_smartcard_c_apdu *apdu, const uint8_t *data, size_t len) +{ + size_t orig_len = len; + if (len < 4) { + return 4; + } + len -= 4; + apdu->class = *data++; + apdu->instruction = *data++; + apdu->params[0] = *data++; + apdu->params[1] = *data++; + + /* figure out the amount of length bytes */ + size_t len_len = 0; + if (!len) { + /* no length */ + len_len = 0; + } else if (!*data && len >= 3) { + /* extended length */ + data++; + len--; + len_len = 2; + } else { + /* short length */ + len_len = 1; + } + + if (len > len_len) { + /* we have Lc */ + apdu->len = 0; + for (size_t i = 0; i < len_len; i++) { + apdu->len <<= 8; + apdu->len |= *data++; + } + len -= len_len; + + if (len < apdu->len) { + return (orig_len - len) + apdu->len; + } + + apdu->data = data; + data += apdu->len; + len -= apdu->len; + } else { + /* no Lc for us */ + apdu->data = NULL; + apdu->len = 0; + } + + if (len) { + /* we have Le */ + if (len < len_len) { + return (orig_len - len) + len_len; + } + apdu->outlen = 0; + for (size_t i = 0; i < len_len; i++) { + apdu->outlen <<= 8; + apdu->outlen |= *data++; + } + if (!apdu->outlen) { + apdu->outlen = 1 << len_len; + } + len -= len_len; + } else { + /* no Le for us */ + apdu->outlen = 0; + } + + return 0; +} + +static inline void r_apdu_init(struct nsl_smartcard_r_apdu *apdu) +{ + apdu->status[0] = NSL_SMARTCARD_APDU_STATUS_ERROR_MISC; + apdu->status[1] = 0; + apdu->len = 0; + apdu->data = NULL; +} + +static inline int r_apdu_write(struct nsl_smartcard_r_apdu *apdu, uint8_t *data, size_t *len) +{ + if (*len < apdu->len + 2) { + return apdu->len + 2; + } + if (apdu->data) { + memcpy(data, apdu->data, apdu->len); + } else { + memset(data, 0, apdu->len); + } + data += apdu->len; + *data++ = apdu->status[0]; + *data++ = apdu->status[1]; + *len = apdu->len + 2; + return 0; +} + + +void nsl_smartcard_apdu_init(struct nsl_smartcard_apdu *apdu) +{ + c_apdu_init(&apdu->command); + r_apdu_init(&apdu->response); +} + +int nsl_smartcard_apdu_read_command(struct nsl_smartcard_apdu *apdu, const uint8_t *data, size_t len) +{ + size_t needed = c_apdu_read(&apdu->command, data, len); + if (needed) { + apdu->response.status[0] = NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_IN_LENGTH; + apdu->response.status[1] = needed; + apdu->response.len = 0; + return NSL_SMARTCARD_ERROR_WRONG_LENGTH; + } + return 0; +} + +int nsl_smartcard_apdu_write_response(struct nsl_smartcard_apdu *apdu, uint8_t *data, size_t *len) +{ + if (apdu->response.len > apdu->command.outlen) { + apdu->response.status[0] = NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_OUT_LENGTH; + apdu->response.status[1] = apdu->response.len; + apdu->response.len = 0; + } + size_t needed = r_apdu_write(&apdu->response, data, len); + if (needed) { + apdu->response.status[0] = NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_OUT_LENGTH; + apdu->response.status[1] = needed; + apdu->response.len = 0; + r_apdu_write(&apdu->response, data, len); + } + return 0; +} diff --git a/src/smartcard/apdu.h b/src/smartcard/apdu.h index 862a0ef..73d965b 100644 --- a/src/smartcard/apdu.h +++ b/src/smartcard/apdu.h @@ -1,21 +1,91 @@ #pragma once +#include +#include -struct nsl_smartcard_apdu { - uint8_t class; - uint8_t instruction; - uint8_t params[2]; -} __attribute__((packed)); +struct nsl_smartcard_c_apdu +{ + uint8_t class; + uint8_t instruction; + uint8_t params[2]; + size_t len; + size_t outlen; + const uint8_t *data; +}; -enum nsl_smartcard_apdu_instruction{ - NSL_SMARTCARD_APDU_INS_VERIFY = 0x20, - NSL_SMARTCARD_APDU_INS_SELECT_DIR = 0xA4, - NSL_SMARTCARD_APDU_INS_DELETE_DIR = 0x0E, - NSL_SMARTCARD_APDU_INS_GET_DATA = 0xCA, - NSL_SMARTCARD_APDU_INS_CREATE_FILE = 0xE0, - NSL_SMARTCARD_APDU_INS_UPDATE_BIN = 0xD6, - NSL_SMARTCARD_APDU_INS_EXECUTE = 0x32, - NSL_SMARTCARD_APDU_INS_NET_LICENSE = 0x3A, - NSL_SMARTCARD_APDU_INS_CONTROL_PIN = 0xD4, +struct nsl_smartcard_r_apdu +{ + uint8_t status[2]; + size_t len; + uint8_t *data; +}; + +struct nsl_smartcard_apdu +{ + struct nsl_smartcard_c_apdu command; + struct nsl_smartcard_r_apdu response; +}; + +enum nsl_smartcard_apdu_instruction { + NSL_SMARTCARD_APDU_INS_DEACTIVATE = 0x04, + NSL_SMARTCARD_APDU_INS_RECORD_ERASE = 0x0C, + NSL_SMARTCARD_APDU_INS_BINARY_ERASE = 0x0E, + NSL_SMARTCARD_APDU_INS_OP_SCOL = 0x10, + NSL_SMARTCARD_APDU_INS_OP_TRANSACTION = 0x12, + NSL_SMARTCARD_APDU_INS_OP_USER = 0x14, + NSL_SMARTCARD_APDU_INS_VERIFY = 0x20, + NSL_SMARTCARD_APDU_INS_ENV_MANAGE = 0x22, + NSL_SMARTCARD_APDU_INS_REFDATA_CHANGE = 0x24, + NSL_SMARTCARD_APDU_INS_VERIFY_DISABLE = 0x26, + NSL_SMARTCARD_APDU_INS_VERIFY_ENABLE = 0x28, + NSL_SMARTCARD_APDU_INS_OP_SECURITY = 0x2A, + NSL_SMARTCARD_APDU_INS_RETRY_RESET = 0x2C, + NSL_SMARTCARD_APDU_INS_ACTIVATE = 0x44, + NSL_SMARTCARD_APDU_INS_KEYPAIR_GEN = 0x46, + NSL_SMARTCARD_APDU_INS_SECRET_IMPORT = 0x48, + NSL_SMARTCARD_APDU_INS_CHANNEL_MANAGE = 0x70, + NSL_SMARTCARD_APDU_INS_AUTH_EXTERNAL = 0x82, + NSL_SMARTCARD_APDU_INS_CHALLENGE_GET = 0x84, + NSL_SMARTCARD_APDU_INS_AUTH_GENERAL = 0x86, + NSL_SMARTCARD_APDU_INS_AUTH_INTERNAL = 0x88, + NSL_SMARTCARD_APDU_INS_BINARY_SEARCH = 0xA0, + NSL_SMARTCARD_APDU_INS_RECORD_SEARCH = 0xA2, + NSL_SMARTCARD_APDU_INS_SELECT = 0xA4, + NSL_SMARTCARD_APDU_INS_BINARY_READ = 0xB0, + NSL_SMARTCARD_APDU_INS_RECORD_READ = 0xB2, + NSL_SMARTCARD_APDU_INS_RESPONSE_GET = 0xC0, + NSL_SMARTCARD_APDU_INS_ENVELOPE = 0xC2, + NSL_SMARTCARD_APDU_INS_DATA_GET = 0xCA, + NSL_SMARTCARD_APDU_INS_DATA_MANAGE = 0xCF, + NSL_SMARTCARD_APDU_INS_BINARY_WRITE = 0xD0, + NSL_SMARTCARD_APDU_INS_RECORD_WRITE = 0xD2, + NSL_SMARTCARD_APDU_INS_BINARY_UPDATE = 0xD6, + NSL_SMARTCARD_APDU_INS_DATA_PUT = 0xDA, + NSL_SMARTCARD_APDU_INS_RECORD_UPDATE = 0xDC, + NSL_SMARTCARD_APDU_INS_CREATE = 0xE0, + NSL_SMARTCARD_APDU_INS_RECORD_APPEND = 0xE2, + NSL_SMARTCARD_APDU_INS_DELETE = 0xE4, + NSL_SMARTCARD_APDU_INS_TERMINATE = 0xE6, + NSL_SMARTCARD_APDU_INS_FILE_TERMINATE = 0xE8, + NSL_SMARTCARD_APDU_INS_TERMINATE_CARD = 0xEF, +}; + +enum nsl_smartcard_apdu_status { + NSL_SMARTCARD_APDU_STATUS_SUCCESS = 0x90, + NSL_SMARTCARD_APDU_STATUS_MORE_DATA = 0x61, + NSL_SMARTCARD_APDU_STATUS_WARNING = 0x62, + NSL_SMARTCARD_APDU_STATUS_WARNING_EFFECT = 0x63, + NSL_SMARTCARD_APDU_STATUS_ERROR = 0x64, + NSL_SMARTCARD_APDU_STATUS_ERROR_EFFECT = 0x65, + NSL_SMARTCARD_APDU_STATUS_ERROR_SECURITY = 0x66, + NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_IN_LENGTH = 0x67, + NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_CLASS = 0x68, + NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_COMMAND = 0x69, + NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_PARAMS = 0x6A, + NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_PARAMS2 = 0x6B, + NSL_SMARTCARD_APDU_STATUS_ERROR_BAD_OUT_LENGTH = 0x6C, + NSL_SMARTCARD_APDU_STATUS_ERROR_UNK_COMMAND = 0x6D, + NSL_SMARTCARD_APDU_STATUS_ERROR_UNK_CLASS = 0x6E, + NSL_SMARTCARD_APDU_STATUS_ERROR_MISC = 0x6F, }; enum nsl_smartcard_apdu_data_tag { @@ -29,3 +99,7 @@ enum nsl_smartcard_apdu_data_tag { NSL_SMARTCARD_APDU_DATA_METADATA = 0xA, NSL_SMARTCARD_APDU_DATA_CURRENT_TIME = 0xB, }; + +void nsl_smartcard_apdu_init(struct nsl_smartcard_apdu *); +int nsl_smartcard_apdu_read_command(struct nsl_smartcard_apdu *, const uint8_t *data, size_t len); +int nsl_smartcard_apdu_write_response(struct nsl_smartcard_apdu *, uint8_t *data, size_t *len); diff --git a/src/smartcard/build.mk b/src/smartcard/build.mk index c37edfe..91be547 100644 --- a/src/smartcard/build.mk +++ b/src/smartcard/build.mk @@ -1,7 +1,9 @@ SRCS += \ + smartcard/apdu.c \ + smartcard/fs.c \ smartcard/emu.c \ - smartcard/fs.o \ - smartcard/smartcard.o + smartcard/commands.c \ + smartcard/smartcard.c OBJS += \ ext/emu8051/libem8051.a @@ -9,5 +11,5 @@ OBJS += \ ext/emu8051/libem8051.a: cd $(dir $@) && make CPPFLAGS=-DEM8051_MINIMAL=1 $(notdir $@) -CPPFLAGS += -I ext/emu8051 +CPPFLAGS += -I ext/emu8051 -DEM8051_MINIMAL=1 diff --git a/src/smartcard/commands.c b/src/smartcard/commands.c new file mode 100644 index 0000000..e69de29 diff --git a/src/smartcard/commands.h b/src/smartcard/commands.h new file mode 100644 index 0000000..e69de29 diff --git a/src/smartcard/error.h b/src/smartcard/error.h index a641c76..3d52d0a 100644 --- a/src/smartcard/error.h +++ b/src/smartcard/error.h @@ -4,6 +4,7 @@ enum nsl_smartcard_error NSL_SMARTCARD_ERROR_WRONG_TYPE, NSL_SMARTCARD_ERROR_WRONG_ID, NSL_SMARTCARD_ERROR_WRONG_VALUE, + NSL_SMARTCARD_ERROR_WRONG_LENGTH, NSL_SMARTCARD_ERROR_NOT_FOUND, NSL_SMARTCARD_ERROR_IN_USE, NSL_SMARTCARD_ERROR_UNAUTHORIZED, diff --git a/src/smartcard/fs.c b/src/smartcard/fs.c index 7aea67d..d8486ad 100644 --- a/src/smartcard/fs.c +++ b/src/smartcard/fs.c @@ -9,12 +9,13 @@ #define NSL_SMARTCARD_DEV_PIN_DEFAULT "123456781234567812345678" #define NSL_SMARTCARD_PIN_LEN_REPORT_CUTOFF 16 -static inline void init_common(struct nsl_smartcard_file *file, uint16_t id, enum nsl_smartcard_file_type type) +static inline void init_common(struct nsl_smartcard_file *file, nsl_smartcart_file_id_t id, enum nsl_smartcard_file_type type) { file->parent = NULL; - file->next = NULL; + file->prev = file->next = NULL; file->id = id; file->type = type; + file->flags = NSL_SMARTCARD_FILE_FLAG_USED; } static inline int is_authorized(struct nsl_smartcard_file *file, enum nsl_smartcard_auth_level level) @@ -22,11 +23,11 @@ static inline int is_authorized(struct nsl_smartcard_file *file, enum nsl_smartc if (!nsl_smartcard_is_dir(file)) { return 0; } - return file->contents.dir.auth >= level; + return (file->flags & NSL_SMARTCARD_FILE_FLAG_AUTH_OVERRIDDEN) || (file->contents.dir.auth >= level); } -int nsl_smartcard_file_init(struct nsl_smartcard_file *file, uint16_t id, enum nsl_smartcard_file_type type) +int nsl_smartcard_file_init(struct nsl_smartcard_file *file, nsl_smartcart_file_id_t id, enum nsl_smartcard_file_type type) { init_common(file, id, type); if (!nsl_smartcard_is_file(file)) { @@ -38,20 +39,21 @@ int nsl_smartcard_file_init(struct nsl_smartcard_file *file, uint16_t id, enum return 0; } -int nsl_smartcard_file_read(struct nsl_smartcard_file *file, const uint8_t **data, size_t *len, enum nsl_smartcard_file_type type) +int nsl_smartcard_file_open(struct nsl_smartcard_file *file, struct nsl_smartcard_handle *handle) { - if (!nsl_smartcard_is_file(file) || file->type != type) { + if (!nsl_smartcard_is_file(file)) { return NSL_SMARTCARD_ERROR_WRONG_TYPE; } + switch (file->type) { case NSL_SMARTCARD_FILE_EXECUTABLE: - if (!is_authorized(file, NSL_SMARTCARD_AUTH_DEV)) { + if (!is_authorized(file->parent, NSL_SMARTCARD_AUTH_DEV)) { return NSL_SMARTCARD_ERROR_UNAUTHORIZED; } break; case NSL_SMARTCARD_FILE_DATA: case NSL_SMARTCARD_FILE_KEY: - if (!is_authorized(file, NSL_SMARTCARD_AUTH_INTERNAL)) { + if (!is_authorized(file->parent, NSL_SMARTCARD_AUTH_INTERNAL)) { return NSL_SMARTCARD_ERROR_UNAUTHORIZED; } break; @@ -59,14 +61,96 @@ int nsl_smartcard_file_read(struct nsl_smartcard_file *file, const uint8_t **dat return NSL_SMARTCARD_ERROR_UNAUTHORIZED; } - *data = file->contents.file.data; - *len = file->contents.file.len; + handle->valid = 1; + handle->file = file; + handle->offset = 0; + return 0; +} + +int nsl_smartcard_file_delete(struct nsl_smartcard_file *file) +{ + if (!nsl_smartcard_is_file(file)) { + return NSL_SMARTCARD_ERROR_WRONG_TYPE; + } + return nsl_smartcard_dir_delete(file->parent, file->id); +} + + +int nsl_smartcard_handle_read(struct nsl_smartcard_handle *handle, uint8_t *data, size_t *len) +{ + if (!nsl_smartcard_handle_is_valid(handle)) { + return NSL_SMARTCARD_ERROR_NOT_FOUND; + } + if (!len) { + return NSL_SMARTCARD_ERROR_WRONG_VALUE; + } + + size_t rlen = nsl_util_min(handle->file->contents.file.len - handle->offset, *len); + if (data) { + memcpy(data, handle->file->contents.file.data + handle->offset, rlen); + } + *len = rlen; + return 0; +} + +int nsl_smartcard_handle_write(struct nsl_smartcard_handle *handle, const uint8_t *data, size_t *len) +{ + if (!nsl_smartcard_handle_is_valid(handle)) { + return NSL_SMARTCARD_ERROR_NOT_FOUND; + } + + size_t rlen = nsl_util_min(handle->file->contents.file.len - handle->offset, *len); + memcpy(handle->file->contents.file.data, data, rlen); + *len = rlen; + return 0; +} + +int nsl_smartcard_handle_seek(struct nsl_smartcard_handle *handle, size_t pos, enum nsl_smartcard_origin origin) +{ + if (!nsl_smartcard_handle_is_valid(handle)) { + return NSL_SMARTCARD_ERROR_NOT_FOUND; + } + + size_t offset; + switch (origin) { + case NSL_SMARTCARD_ORIGIN_START: + offset = pos; + break; + case NSL_SMARTCARD_ORIGIN_CURRENT: + offset = handle->offset + pos; + break; + case NSL_SMARTCARD_ORIGIN_END: + offset = handle->file->contents.file.len + pos; + break; + default: + return NSL_SMARTCARD_ERROR_WRONG_VALUE; + } + + handle->offset = nsl_util_min(offset, handle->file->contents.file.len); + return 0; +} + +int nsl_smartcard_handle_tell(struct nsl_smartcard_handle *handle, size_t *pos) +{ + if (!nsl_smartcard_handle_is_valid(handle)) { + return NSL_SMARTCARD_ERROR_NOT_FOUND; + } + *pos = handle->offset; + return 0; +} + +int nsl_smartcard_handle_close(struct nsl_smartcard_handle *handle) +{ + if (!nsl_smartcard_handle_is_valid(handle)) { + return NSL_SMARTCARD_ERROR_NOT_FOUND; + } + handle->valid = 0; + handle->file = NULL; return 0; } - -int nsl_smartcard_dir_init(struct nsl_smartcard_file *file, uint16_t id) +int nsl_smartcard_dir_init(struct nsl_smartcard_file *file, nsl_smartcart_file_id_t id) { init_common(file, id, NSL_SMARTCARD_FILE_DIR); @@ -151,33 +235,54 @@ int nsl_smartcard_dir_change_pin(struct nsl_smartcard_file *file, const uint8_t return 0; } +int nsl_smartcard_dir_internal_begin(struct nsl_smartcard_file *file) +{ + if (!nsl_smartcard_is_dir(file)) { + return NSL_SMARTCARD_ERROR_WRONG_TYPE; + } + file->flags |= NSL_SMARTCARD_FILE_FLAG_AUTH_OVERRIDDEN; + return 0; +} + +int nsl_smartcard_dir_internal_end(struct nsl_smartcard_file *file) +{ + if (!nsl_smartcard_is_dir(file)) { + return NSL_SMARTCARD_ERROR_WRONG_TYPE; + } + file->flags |= NSL_SMARTCARD_FILE_FLAG_AUTH_OVERRIDDEN; + return 0; +} + + int nsl_smartcard_dir_add(struct nsl_smartcard_file *file, struct nsl_smartcard_file *child) { if (!nsl_smartcard_is_dir(file)) { return NSL_SMARTCARD_ERROR_WRONG_TYPE; } - if (file->id == NSL_SMARTCARD_ROOT_DIR_ID) { - return NSL_SMARTCARD_ERROR_WRONG_ID; - } if (!is_authorized(file, NSL_SMARTCARD_AUTH_DEV)) { return NSL_SMARTCARD_ERROR_UNAUTHORIZED; } - if (child->parent || child->next) { + if (child->id == NSL_SMARTCARD_ROOT_DIR_ID) { + return NSL_SMARTCARD_ERROR_WRONG_ID; + } + if (child->parent || child->prev || child->next) { return NSL_SMARTCARD_ERROR_IN_USE; } if (file->id == child->id || (file->parent && file->parent->id == child->id)) { return NSL_SMARTCARD_ERROR_WRONG_ID; } - if (nsl_smartcard_dir_find(file, child->id, NULL)) { + if (!nsl_smartcard_dir_find(file, child->id, NULL)) { return NSL_SMARTCARD_ERROR_WRONG_ID; } child->parent = file; + child->prev = NULL; child->next = file->contents.dir.children; + file->contents.dir.children->prev = child; file->contents.dir.children = child; return 0; } -int nsl_smartcard_dir_find(struct nsl_smartcard_file *file, uint16_t id, struct nsl_smartcard_file **child) +int nsl_smartcard_dir_find(struct nsl_smartcard_file *file, nsl_smartcart_file_id_t id, struct nsl_smartcard_file **child) { if (!nsl_smartcard_is_dir(file)) { return NSL_SMARTCARD_ERROR_WRONG_TYPE; @@ -193,6 +298,38 @@ int nsl_smartcard_dir_find(struct nsl_smartcard_file *file, uint16_t id, struct return NSL_SMARTCARD_ERROR_NOT_FOUND; } +int nsl_smartcard_dir_delete(struct nsl_smartcard_file *file, nsl_smartcart_file_id_t id) +{ + if (!nsl_smartcard_is_dir(file)) { + return NSL_SMARTCARD_ERROR_WRONG_TYPE; + } + if (!is_authorized(file, NSL_SMARTCARD_AUTH_DEV)) { + return NSL_SMARTCARD_ERROR_UNAUTHORIZED; + } + + struct nsl_smartcard_file *child; + if (!nsl_smartcard_dir_find(file, id, &child)) { + return NSL_SMARTCARD_ERROR_WRONG_ID; + } + + if (child->prev) { + child->prev->next = child->next; + } else { + file->contents.dir.children = child->next; + } + if (child->next) { + child->next->prev = child->prev; + } + + child->parent = NULL; + child->prev = child->next = NULL; + child->flags &= ~NSL_SMARTCARD_FILE_FLAG_USED; + if (child->flags & NSL_SMARTCARD_FILE_FLAG_ALLOCATED) { + free(child); + } + return 0; +} + int nsl_smartcard_dir_clear(struct nsl_smartcard_file *file) { if (!nsl_smartcard_is_dir(file)) { @@ -202,18 +339,15 @@ int nsl_smartcard_dir_clear(struct nsl_smartcard_file *file) return NSL_SMARTCARD_ERROR_UNAUTHORIZED; } - struct nsl_smartcard_file *prev = NULL, *f = file->contents.dir.children; + struct nsl_smartcard_file *f = file->contents.dir.children, *next; while (f) { + next = f->next; if (nsl_smartcard_is_dir(f)) { nsl_smartcard_dir_clear(f); - prev = f; - f = f->next; } else { - struct nsl_smartcard_file *next = f->next; - free(f); - prev->next = f; - f = next; + nsl_smartcard_file_delete(f); } + f = next; } return 0; } diff --git a/src/smartcard/fs.h b/src/smartcard/fs.h index ee80970..5e7a118 100644 --- a/src/smartcard/fs.h +++ b/src/smartcard/fs.h @@ -5,8 +5,11 @@ #define NSL_SMARTCARD_USER_PIN_LEN 8 #define NSL_SMARTCARD_DEV_PIN_LEN 24 #define NSL_SMARTCARD_ROOT_DIR_ID 0x3F00 +#define NSL_SMARTCARD_CUR_DIR_ID 0x3FFF +typedef uint16_t nsl_smartcart_file_id_t; + enum nsl_smartcard_auth_level { NSL_SMARTCARD_AUTH_NONE, @@ -24,6 +27,15 @@ enum nsl_smartcard_file_type NSL_SMARTCARD_FILE_MODULE, }; +enum nsl_smartcard_file_flags +{ + NSL_SMARTCARD_FILE_FLAG_ENABLED, + NSL_SMARTCARD_FILE_FLAG_INTERNAL, + NSL_SMARTCARD_FILE_FLAG_USED, + NSL_SMARTCARD_FILE_FLAG_ALLOCATED, + NSL_SMARTCARD_FILE_FLAG_AUTH_OVERRIDDEN, +}; + struct nsl_smartcard_file_contents { uint8_t *data; @@ -35,6 +47,7 @@ struct nsl_smartcard_dir_contents struct nsl_smartcard_file *children; uint8_t user_pin[NSL_SMARTCARD_USER_PIN_LEN]; uint8_t dev_pin[NSL_SMARTCARD_DEV_PIN_LEN]; + enum nsl_smartcard_auth_level auth; uint8_t auth_attempts; }; @@ -42,11 +55,12 @@ struct nsl_smartcard_dir_contents struct nsl_smartcard_file { struct nsl_smartcard_file *parent; - struct nsl_smartcard_file *next; + struct nsl_smartcard_file *prev, *next; enum nsl_smartcard_file_type type; + int flags; - uint16_t id; + nsl_smartcart_file_id_t id; union { struct nsl_smartcard_file_contents file; struct nsl_smartcard_dir_contents dir; @@ -70,12 +84,43 @@ static inline int nsl_smartcard_is_file(struct nsl_smartcard_file *file) } } -int nsl_smartcard_file_init(struct nsl_smartcard_file *, uint16_t id, enum nsl_smartcard_file_type type); -int nsl_smartcard_file_read(struct nsl_smartcard_file *, const uint8_t **data, size_t *len, enum nsl_smartcard_file_type type); +enum nsl_smartcard_origin +{ + NSL_SMARTCARD_ORIGIN_START, + NSL_SMARTCARD_ORIGIN_CURRENT, + NSL_SMARTCARD_ORIGIN_END, +}; -int nsl_smartcard_dir_init(struct nsl_smartcard_file *, uint16_t id); +struct nsl_smartcard_handle +{ + struct nsl_smartcard_file *file; + size_t offset; + int valid; +}; + + +int nsl_smartcard_file_init(struct nsl_smartcard_file *, nsl_smartcart_file_id_t id, enum nsl_smartcard_file_type type); +int nsl_smartcard_file_open(struct nsl_smartcard_file *, struct nsl_smartcard_handle *handle); +int nsl_smartcard_file_delete(struct nsl_smartcard_file *); + +static inline int nsl_smartcard_handle_is_valid(struct nsl_smartcard_handle *handle) +{ + return handle->valid; +} + +int nsl_smartcard_handle_read(struct nsl_smartcard_handle *, uint8_t *data, size_t *len); +int nsl_smartcard_handle_write(struct nsl_smartcard_handle *, const uint8_t *data, size_t *len); +int nsl_smartcard_handle_seek(struct nsl_smartcard_handle *, size_t pos, enum nsl_smartcard_origin origin); +int nsl_smartcard_handle_tell(struct nsl_smartcard_handle *, size_t *pos); +int nsl_smartcard_handle_close(struct nsl_smartcard_handle *); + +int nsl_smartcard_dir_init(struct nsl_smartcard_file *, nsl_smartcart_file_id_t id); int nsl_smartcard_dir_authorize(struct nsl_smartcard_file *, const uint8_t *pin, size_t len, enum nsl_smartcard_auth_level level); int nsl_smartcard_dir_change_pin(struct nsl_smartcard_file *, const uint8_t *pin, size_t len, enum nsl_smartcard_auth_level level); +int nsl_smartcard_dir_internal_begin(struct nsl_smartcard_file *); +int nsl_smartcard_dir_internal_end(struct nsl_smartcard_file *); + int nsl_smartcard_dir_add(struct nsl_smartcard_file *, struct nsl_smartcard_file *child); -int nsl_smartcard_dir_find(struct nsl_smartcard_file *, uint16_t id, struct nsl_smartcard_file **child); +int nsl_smartcard_dir_find(struct nsl_smartcard_file *, nsl_smartcart_file_id_t id, struct nsl_smartcard_file **child); +int nsl_smartcard_dir_delete(struct nsl_smartcard_file *, nsl_smartcart_file_id_t id); int nsl_smartcard_dir_clear(struct nsl_smartcard_file *); diff --git a/src/smartcard/smartcard.c b/src/smartcard/smartcard.c index 76546cf..d55ae9e 100644 --- a/src/smartcard/smartcard.c +++ b/src/smartcard/smartcard.c @@ -4,23 +4,24 @@ #include "error.h" #include "../util.h" -#define NSL_SMARTCARD_DIR_SEP "\\" - -static int parse_file_name(const char *name, uint16_t *id) +static void set_error(struct nsl_smartcard *smartcard, uint8_t sw1, uint8_t sw2) { - uint8_t pname[2]; - if (nsl_util_fromhex(name, pname, sizeof(pname))) { - return 0; - } - *id = (pname[0] << 8) | pname[1]; - return 1; + smartcard->apdu.response.status[0] = sw1; + smartcard->apdu.response.status[1] = sw2; + smartcard->apdu.response.len = 0; } int nsl_smartcard_init(struct nsl_smartcard *smartcard) { smartcard->rootdir = smartcard->curdir = NULL; + memset(smartcard->handles, 0, sizeof(smartcard->handles)); + smartcard->last_handle = 0; + + memset(smartcard->standard_handlers, 0, sizeof(smartcard->standard_handlers)); + memset(smartcard->proprietary_handlers, 0, sizeof(smartcard->proprietary_handlers)); + nsl_smartcard_emu_init(&smartcard->emu, 0xFFFF, 0xFFFF); return nsl_smartcard_reset(smartcard); } @@ -49,12 +50,8 @@ int nsl_smartcard_change_pin(struct nsl_smartcard *smartcard, const uint8_t *pin } -int nsl_smartcard_mkdir(struct nsl_smartcard *smartcard, const char *name) +int nsl_smartcard_mkdir(struct nsl_smartcard *smartcard, nsl_smartcart_file_id_t id) { - uint16_t id; - if (!parse_file_name(name, &id)) { - return NSL_SMARTCARD_ERROR_WRONG_VALUE; - } if (!smartcard->curdir) { return NSL_SMARTCARD_ERROR_NOT_FOUND; } @@ -83,58 +80,92 @@ err: return err; } -int nsl_smartcard_chdir(struct nsl_smartcard *smartcard, char *name) +int nsl_smartcard_chdir(struct nsl_smartcard *smartcard, nsl_smartcart_file_id_t id) { struct nsl_smartcard_file *dir; - if (!strcmp(name, NSL_SMARTCARD_DIR_SEP)) { - dir = smartcard->rootdir; - name += sizeof(NSL_SMARTCARD_DIR_SEP) - 1; - } else { + if (id == NSL_SMARTCARD_CUR_DIR_ID) { dir = smartcard->curdir; - } - if (!dir) { - return NSL_SMARTCARD_ERROR_NOT_FOUND; - } - - do { - char *seg = strsep(&name, NSL_SMARTCARD_DIR_SEP); - uint16_t id; - if (!parse_file_name(seg, &id)) { - return NSL_SMARTCARD_ERROR_WRONG_VALUE; - } - if (nsl_smartcard_dir_find(dir, id, &dir)) { + } else if (id == NSL_SMARTCARD_ROOT_DIR_ID) { + dir = smartcard->rootdir; + } else { + if (!smartcard->curdir) { return NSL_SMARTCARD_ERROR_NOT_FOUND; } - if (!nsl_smartcard_is_dir(dir)) { - return NSL_SMARTCARD_ERROR_WRONG_TYPE; + int err = nsl_smartcard_dir_find(smartcard->curdir, id, &dir); + if (err) { + return err; } - } while (name); + } smartcard->curdir = dir; return 0; } -int nsl_smartcard_exec(struct nsl_smartcard *smartcard, const char *name, const uint8_t *inbuf, size_t inlen, uint8_t *outbuf, size_t outlen) +int nsl_smartcard_exec(struct nsl_smartcard *smartcard, nsl_smartcart_file_id_t id, const uint8_t *inbuf, size_t inlen, uint8_t *outbuf, size_t outlen) { - uint16_t id; - if (!parse_file_name(name, &id)) { - return NSL_SMARTCARD_ERROR_WRONG_VALUE; - } if (!smartcard->curdir) { return NSL_SMARTCARD_ERROR_NOT_FOUND; } - struct nsl_smartcard_file *file; - if (nsl_smartcard_dir_find(smartcard->curdir, id, &file)) { - return NSL_SMARTCARD_ERROR_NOT_FOUND; - } - const uint8_t *data; - size_t len; - int err = nsl_smartcard_file_read(file, &data, &len, NSL_SMARTCARD_FILE_EXECUTABLE); + struct nsl_smartcard_handle *handle; + int err = nsl_smartcard_open(smartcard, id, &handle); if (err) { return err; } + /* TODO: setup VM and run. */ return 0; } + + +int nsl_smartcard_open(struct nsl_smartcard *smartcard, nsl_smartcart_file_id_t id, struct nsl_smartcard_handle **handle) +{ + size_t nhandles = sizeof(smartcard->handles) / sizeof(*smartcard->handles); + size_t cur = smartcard->last_handle + 1; + for (size_t i = 0; i < nhandles; i++, cur++) { + cur %= nhandles; + if (!nsl_smartcard_handle_is_valid(&smartcard->handles[cur])) { + break; + } + } + + struct nsl_smartcard_handle *h = &smartcard->handles[cur]; + if (nsl_smartcard_handle_is_valid(h)) { + return NSL_SMARTCARD_ERROR_NO_MEMORY; + } + + struct nsl_smartcard_file *file; + int err = nsl_smartcard_dir_find(smartcard->curdir, id, &file); + if (err) { + return err; + } + err = nsl_smartcard_file_open(file, h); + if (err) { + return err; + } + smartcard->last_handle = cur; + *handle = h; + return 0; +} + + +int nsl_smartcard_process(struct nsl_smartcard *smartcard, const uint8_t *inbuf, size_t inlen, uint8_t *outbuf, size_t outlen) +{ + int err = nsl_smartcard_apdu_read_command(&smartcard->apdu, inbuf, inlen); + if (!err) { + nsl_smartcard_handler_t handler; + if (smartcard->apdu.command.class & 0x80) { + handler = smartcard->proprietary_handlers[smartcard->apdu.command.instruction]; + } else { + handler = smartcard->standard_handlers[smartcard->apdu.command.instruction]; + } + if (handler) { + err = handler(smartcard, &smartcard->apdu); + } else { + set_error(smartcard, NSL_SMARTCARD_APDU_STATUS_ERROR_UNK_COMMAND, 0); + } + } + nsl_smartcard_apdu_write_response(&smartcard->apdu, outbuf, &outlen); + return err; +} diff --git a/src/smartcard/smartcard.h b/src/smartcard/smartcard.h index 25790ab..3865f66 100644 --- a/src/smartcard/smartcard.h +++ b/src/smartcard/smartcard.h @@ -1,13 +1,29 @@ #pragma once #include +#include "apdu.h" #include "fs.h" #include "emu.h" + +#define NSL_SMARTCARD_MAX_HANDLES 16 + + +struct nsl_smartcard; + +typedef int (*nsl_smartcard_handler_t)(struct nsl_smartcard *smartcard, struct nsl_smartcard_apdu *apdu); + struct nsl_smartcard { + struct nsl_smartcard_apdu apdu; struct nsl_smartcard_file *rootdir; struct nsl_smartcard_file *curdir; struct nsl_smartcard_emu emu; + + nsl_smartcard_handler_t standard_handlers[0x100]; + nsl_smartcard_handler_t proprietary_handlers[0x100]; + + struct nsl_smartcard_handle handles[NSL_SMARTCARD_MAX_HANDLES]; + uint8_t last_handle; }; int nsl_smartcard_init(struct nsl_smartcard *smartcard); @@ -15,8 +31,9 @@ int nsl_smartcard_reset(struct nsl_smartcard *smartcard); int nsl_smartcard_authorize(struct nsl_smartcard *smartcard, const uint8_t *pin, size_t len, enum nsl_smartcard_auth_level level); int nsl_smartcard_change_pin(struct nsl_smartcard *smartcard, const uint8_t *pin, size_t len, enum nsl_smartcard_auth_level level); -int nsl_smartcard_mkdir(struct nsl_smartcard *smartcard, const char *dir); -int nsl_smartcard_chdir(struct nsl_smartcard *smartcard, char *name); -int nsl_smartcard_exec(struct nsl_smartcard *smarcard, const char *name, const uint8_t *inbuf, size_t inlen, uint8_t *outbuf, size_t outlen); +int nsl_smartcard_mkdir(struct nsl_smartcard *smartcard, nsl_smartcart_file_id_t id); +int nsl_smartcard_chdir(struct nsl_smartcard *smartcard, nsl_smartcart_file_id_t id); +int nsl_smartcard_exec(struct nsl_smartcard *smartcard, nsl_smartcart_file_id_t id, const uint8_t *inbuf, size_t inlen, uint8_t *outbuf, size_t outlen); +int nsl_smartcard_open(struct nsl_smartcard *smartcard, nsl_smartcart_file_id_t id, struct nsl_smartcard_handle **handle); int nsl_smartcard_process(struct nsl_smartcard *smartcard, const uint8_t *inbuf, size_t inlen, uint8_t *outbuf, size_t outlen); diff --git a/src/util.h b/src/util.h index ce2d33e..1996460 100644 --- a/src/util.h +++ b/src/util.h @@ -5,28 +5,37 @@ int nsl_util_memcmp(const uint8_t *left, const uint8_t *right, size_t len); static inline int nsl_util_fromhexdigit(int c) { - c = tolower(c); - if (c >= '0' && c <= '9') { - return c - '0'; - } - if (c >= 'a' && c <= 'f') { - return 10 + (c - 'a'); - } - return -1; + c = tolower(c); + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return 10 + (c - 'a'); + } + return -1; } static inline int nsl_util_fromhex(const char *src, unsigned char *dest, size_t len) { - while (*src && len--) { - unsigned char a = nsl_util_fromhexdigit(*src++); - if (a < 0) { - return 0; + while (*src && len--) { + unsigned char a = nsl_util_fromhexdigit(*src++); + if (a < 0) { + return 0; + } + unsigned char b = nsl_util_fromhexdigit(*src++); + if (b < 0) { + return 0; + } + *dest++ = (a << 4) | b; + } + return !*src && !len; +} + +static inline int nsl_util_min(size_t a, size_t b) +{ + if (a > b) { + return a; + } else { + return b; } - unsigned char b = nsl_util_fromhexdigit(*src++); - if (b < 0) { - return 0; - } - *dest++ = (a << 4) | b; - } - return !*src && !len; }