smartcard: implement more file system and APDU things

This commit is contained in:
Shiz 2020-04-12 22:20:21 +02:00
parent d0bc7fc18f
commit ce8a29ef35
12 changed files with 573 additions and 118 deletions

View File

@ -1,4 +1,4 @@
CFLAGS = -std=c99
CFLAGS = -std=c11 -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-empty-translation-unit
SRCS =
OBJS =
BIN = nonsenselock

142
src/smartcard/apdu.c Normal file
View File

@ -0,0 +1,142 @@
#include <string.h>
#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;
}

View File

@ -1,21 +1,91 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
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);

View File

@ -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

0
src/smartcard/commands.c Normal file
View File

0
src/smartcard/commands.h Normal file
View File

View File

@ -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,

View File

@ -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;
}

View File

@ -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 *);

View File

@ -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;
}

View File

@ -1,13 +1,29 @@
#pragma once
#include <stdint.h>
#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);

View File

@ -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;
}