Files
bananatools/iccard/felica.c

348 lines
7.1 KiB
C

#include <windows.h>
#include <stdlib.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#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_active(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
static HRESULT felica_cmd_read_without_encryption(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
static HRESULT felica_cmd_write_without_encryption(
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 current value has been taken from an iPhone, emulating
a Suica pass via Apple Wallet, which seems to be one of the few
universally accepted FeliCa types for these games. Certain older
Suica passes and other payment and transportation cards
do not seem to be supported anymore. */
return 0x01168B868FBECBFFULL;
}
uint64_t felica_get_amusement_ic_PMm(void)
{
/*
* AIC Card PMm, if this is returned from the card,
* the aimelib will access the actual blocks for authentication.
*/
return 0x00F1000000014300;
}
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_READ_WITHOUT_ENCRYPTION:
return felica_cmd_read_without_encryption(f, req, res);
case FELICA_WRITE_WITHOUT_ENCRYPTION:
return felica_cmd_write_without_encryption(f, req, res);
case FELICA_CMD_ACTIVE:
return felica_cmd_active(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_active(
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);
}
static HRESULT felica_cmd_read_without_encryption(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
HRESULT hr;
uint8_t system_code_count;
uint16_t* system_codes;
uint8_t read_block_count;
uint8_t* blocks;
size_t i;
hr = iobuf_read_8(req, &system_code_count);
if (FAILED(hr)) {
goto fail;
}
system_codes = malloc(sizeof(uint16_t) * system_code_count);
if (!system_codes) goto fail;
for (i = 0; i < system_code_count; i++) {
hr = iobuf_read_be16(req, system_codes + i);
if (FAILED(hr)) {
goto fail;
}
}
hr = iobuf_read_8(req, &read_block_count);
if (FAILED(hr)) {
goto fail;
}
blocks = malloc(read_block_count);
if (!system_codes) goto fail;
for (i = 0; i < read_block_count; i++) {
// 0x80
hr = iobuf_read_8(req, blocks + i);
if (FAILED(hr)) {
goto fail;
}
// actual block num
hr = iobuf_read_8(req, blocks + i);
if (FAILED(hr)) {
goto fail;
}
}
// status
hr = iobuf_write_be16(res, 0);
if (FAILED(hr)) {
goto fail;
}
// block count
hr = iobuf_write_8(res, read_block_count);
if (FAILED(hr)) {
goto fail;
}
// block data
for (i = 0; i < read_block_count; i++)
{
dprintf("FeliCa: Read block %x\n", blocks[i]);
switch (blocks[i]) {
case 0x82: {
hr = iobuf_write_be64(res, f->IDm);
if (FAILED(hr))
{
goto fail;
}
hr = iobuf_write_be64(res, 0x0078000000000000ull);
if (FAILED(hr))
{
goto fail;
}
}
default: {
hr = iobuf_write_be64(res, 0);
if (FAILED(hr))
{
goto fail;
}
hr = iobuf_write_be64(res, 0);
if (FAILED(hr))
{
goto fail;
}
}
}
}
hr = S_OK;
fail:
if (system_codes) free(system_codes);
if (blocks) free(blocks);
return hr;
}
static HRESULT felica_cmd_write_without_encryption(
struct felica* f,
struct const_iobuf* req,
struct iobuf* res)
{
return iobuf_write_be16(res, 0);
}