From a09843cbd632e947b6842130d2cf5caeae283769 Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 19 Oct 2019 15:48:54 -0400 Subject: [PATCH] iccard/felica.c: Add initial FeliCa IC emulator --- iccard/felica.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ iccard/felica.h | 27 +++++++ iccard/meson.build | 2 + 3 files changed, 221 insertions(+) create mode 100644 iccard/felica.c create mode 100644 iccard/felica.h diff --git a/iccard/felica.c b/iccard/felica.c new file mode 100644 index 0000000..ba71232 --- /dev/null +++ b/iccard/felica.c @@ -0,0 +1,192 @@ +#include + +#include +#include +#include + +#include "hook/iobuf.h" + +#include "iccard/felica.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT felica_cmd_poll( + struct felica *f, + struct const_iobuf *req, + struct iobuf *res); + +static HRESULT felica_cmd_get_system_code( + struct felica *f, + struct const_iobuf *req, + struct iobuf *res); + +static HRESULT felica_cmd_nda_a4( + struct felica *f, + struct const_iobuf *req, + struct iobuf *res); + +uint64_t felica_get_generic_PMm(void) +{ + /* A FeliCa PMm contains low-level protocol timing information for + communicating with a particular IC card. The exact values are not + particularly important for our purposes, so we'll just return a hard- + coded PMm. This was taken from a SuiCa train pass I bought in Japan. */ + + return 0x053143454682B7FFULL; +} + +HRESULT felica_transact( + struct felica *f, + struct const_iobuf *req, + struct iobuf *res) +{ + uint64_t IDm; + uint8_t code; + HRESULT hr; + + assert(f != NULL); + assert(req != NULL); + assert(res != NULL); + + hr = iobuf_read_8(req, &code); + + if (FAILED(hr)) { + return hr; + } + + hr = iobuf_write_8(res, code + 1); + + if (FAILED(hr)) { + return hr; + } + + if (code != FELICA_CMD_POLL) { + hr = iobuf_read_be64(req, &IDm); + + if (FAILED(hr)) { + return hr; + } + + if (IDm != f->IDm) { + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + hr = iobuf_write_be64(res, IDm); + + if (FAILED(hr)) { + return hr; + } + } + + switch (code) { + case FELICA_CMD_POLL: + return felica_cmd_poll(f, req, res); + + case FELICA_CMD_GET_SYSTEM_CODE: + return felica_cmd_get_system_code(f, req, res); + + case FELICA_CMD_NDA_A4: + return felica_cmd_nda_a4(f, req, res); + + default: + dprintf("FeliCa: Unimplemented command %02x, payload:\n", code); + dump_const_iobuf(req); + + return E_NOTIMPL; + } +} + +static HRESULT felica_cmd_poll( + struct felica *f, + struct const_iobuf *req, + struct iobuf *res) +{ + uint16_t system_code; + uint8_t request_code; + uint8_t time_slot; + HRESULT hr; + + /* Request: */ + + hr = iobuf_read_be16(req, &system_code); + + if (FAILED(hr)) { + return hr; + } + + hr = iobuf_read_8(req, &request_code); + + if (FAILED(hr)) { + return hr; + } + + hr = iobuf_read_8(req, &time_slot); + + if (FAILED(hr)) { + return hr; + } + + if (system_code != 0xFFFF && system_code != f->system_code) { + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + // TODO handle other params correctly... + + /* Response: */ + + hr = iobuf_write_be64(res, f->IDm); + + if (FAILED(hr)) { + return hr; + } + + hr = iobuf_write_be64(res, f->PMm); + + if (FAILED(hr)) { + return hr; + } + + if (request_code == 0x01) { + hr = iobuf_write_be16(res, f->system_code); + + if (FAILED(hr)) { + return hr; + } + } + + return S_OK; +} + +static HRESULT felica_cmd_get_system_code( + struct felica *f, + struct const_iobuf *req, + struct iobuf *res) +{ + HRESULT hr; + + hr = iobuf_write_8(res, 1); /* Number of system codes */ + + if (FAILED(hr)) { + return hr; + } + + hr = iobuf_write_be16(res, f->system_code); + + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} + +static HRESULT felica_cmd_nda_a4( + struct felica *f, + struct const_iobuf *req, + struct iobuf *res) +{ + /* The specification for this command is probably only available under NDA. + Returning what the driver seems to want. */ + + return iobuf_write_8(res, 0); +} diff --git a/iccard/felica.h b/iccard/felica.h new file mode 100644 index 0000000..33be201 --- /dev/null +++ b/iccard/felica.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include +#include + +#include "hook/iobuf.h" + +enum { + FELICA_CMD_POLL = 0x00, + FELICA_CMD_GET_SYSTEM_CODE = 0x0c, + FELICA_CMD_NDA_A4 = 0xa4, +}; + +struct felica { + uint64_t IDm; + uint64_t PMm; + uint16_t system_code; +}; + +HRESULT felica_transact( + struct felica *f, + struct const_iobuf *req, + struct iobuf *res); + +uint64_t felica_get_generic_PMm(void); diff --git a/iccard/meson.build b/iccard/meson.build index c01d505..1da0d1e 100644 --- a/iccard/meson.build +++ b/iccard/meson.build @@ -7,5 +7,7 @@ iccard_lib = static_library( capnhook.get_variable('hook_dep'), ], sources : [ + 'felica.c', + 'felica.h', ], )