1
0
mirror of https://github.com/djhackersdev/bemanitools.git synced 2024-11-15 02:37:37 +01:00

acioemu: add support for 1.7.0 and encrypted polls

This commit is contained in:
Will Xyen 2020-06-13 21:52:24 -07:00 committed by adfe3beeadc615e0fa48b493761cc42aae47d399
parent e5331fbc7c
commit 7d533f60cb
5 changed files with 189 additions and 10 deletions

View File

@ -12,6 +12,8 @@ enum ac_io_icca_cmd {
AC_IO_ICCA_CMD_SET_SLOT_STATE = 0x0135,
AC_IO_ICCA_CMD_BEGIN_KEYPAD = 0x013A,
AC_IO_ICCA_CMD_POLL_FELICA = 0x0161,
AC_IO_ICCA_CMD_KEY_EXCHANGE = 0x0160,
AC_IO_ICCA_CMD_POLL_ENCRYPTED = 0x0164,
};
enum ac_io_icca_slot_state {

View File

@ -14,6 +14,7 @@
#include "bemanitools/eamio.h"
#include "util/crc.h"
#include "util/time.h"
enum ac_io_icca_subcmd {
@ -41,7 +42,8 @@ static void ac_io_emu_icca_cmd_send_version(
static void ac_io_emu_icca_send_state(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req,
uint64_t delay_us);
uint64_t delay_us,
bool encrypted);
static void ac_io_emu_icca_send_empty(
struct ac_io_emu_icca *icca, const struct ac_io_message *req);
@ -51,6 +53,12 @@ static void ac_io_emu_icca_send_status(
const struct ac_io_message *req,
uint8_t status);
static void ac_io_emu_icca_cipher(
struct ac_io_emu_icca *icca, uint8_t *data, size_t length);
static void ac_io_emu_icca_cipher_set_key(
struct ac_io_emu_icca *icca, const struct ac_io_message *req);
void ac_io_emu_icca_init(
struct ac_io_emu_icca *icca, struct ac_io_emu *emu, uint8_t unit_no)
{
@ -59,6 +67,15 @@ void ac_io_emu_icca_init(
icca->unit_no = unit_no;
// queue must be started
icca->fault = true;
// default to 1.6.0
icca->version = v160;
}
void ac_io_emu_icca_set_version(
struct ac_io_emu_icca *icca, enum ac_io_emu_icca_version version)
{
icca->version = version;
}
void ac_io_emu_icca_dispatch_request(
@ -123,7 +140,7 @@ void ac_io_emu_icca_dispatch_request(
break;
case AC_IO_ICCA_CMD_ENGAGE:
ac_io_emu_icca_send_state(icca, req, 0);
ac_io_emu_icca_send_state(icca, req, 0, false);
break;
@ -174,6 +191,7 @@ void ac_io_emu_icca_dispatch_request(
break;
}
case AC_IO_ICCA_CMD_POLL_ENCRYPTED:
case AC_IO_ICCA_CMD_POLL:
delay_us = time_get_elapsed_us(
time_get_counter() - icca->time_counter_last_poll);
@ -185,13 +203,26 @@ void ac_io_emu_icca_dispatch_request(
}
icca->time_counter_last_poll = time_get_counter();
ac_io_emu_icca_send_state(icca, req, delay_us);
bool encrypted = cmd_code == AC_IO_ICCA_CMD_POLL_ENCRYPTED;
ac_io_emu_icca_send_state(icca, req, delay_us, encrypted);
break;
case AC_IO_ICCA_CMD_POLL_FELICA:
icca->detected_new_reader = true;
ac_io_emu_icca_send_status(icca, req, 0x01);
if (icca->version == v170) {
ac_io_emu_icca_send_empty(icca, req);
} else {
ac_io_emu_icca_send_status(icca, req, 0x01);
}
break;
case AC_IO_ICCA_CMD_KEY_EXCHANGE:
icca->detected_new_reader = true;
log_misc("AC_IO_ICCA_CMD_KEY_EXCHANGE(%d)", req->addr);
ac_io_emu_icca_cipher_set_key(icca, req);
break;
@ -215,10 +246,27 @@ static void ac_io_emu_icca_cmd_send_version(
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.version);
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_ICCA);
resp.cmd.version.flag = 0x00;
resp.cmd.version.major = 0x01;
resp.cmd.version.minor = 0x06;
if (icca->version == v150) {
log_warning(
"ICCA v1.5.0 emulation requested, please remove this log once implemented");
resp.cmd.version.minor = 0x05;
} else if (icca->version == v160) {
resp.cmd.version.minor = 0x06;
} else if (icca->version == v170) {
resp.cmd.version.minor = 0x07;
} else {
// probably log invalid version here
log_warning(
"Unknown ICCA version: %d emulation requested",
icca->version);
}
resp.cmd.version.revision = 0x00;
memcpy(
resp.cmd.version.product_code,
"ICCA",
@ -261,7 +309,8 @@ static void ac_io_emu_icca_send_status(
static void ac_io_emu_icca_send_state(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req,
uint64_t delay_us)
uint64_t delay_us,
bool encrypted)
{
struct ac_io_message resp;
struct ac_io_icca_state *body;
@ -319,7 +368,10 @@ static void ac_io_emu_icca_send_state(
} else if (card_full_insert) {
body->status_code = AC_IO_ICCA_STATUS_GOT_UID;
} else {
if (icca->detected_new_reader) {
if (icca->detected_new_reader && icca->version == v170) {
// else we get a power failure?
body->status_code = 0;
} else if (icca->detected_new_reader) {
body->status_code = AC_IO_ICCA_STATUS_IDLE_NEW;
} else {
body->status_code = AC_IO_ICCA_STATUS_IDLE;
@ -384,5 +436,88 @@ static void ac_io_emu_icca_send_state(
body->status_code = AC_IO_ICCA_STATUS_FAULT;
}
if (encrypted) {
size_t base_state_sz = sizeof(struct ac_io_icca_state);
resp.cmd.nbytes = base_state_sz + 2;
if (!icca->cipher_started) {
log_warning("No crypto keys set for unit %d", icca->unit_no);
} else {
uint16_t crc = crc16_msb(resp.cmd.raw, base_state_sz, 0);
resp.cmd.raw[base_state_sz + 0] = (crc >> 8) & 0xFF;
resp.cmd.raw[base_state_sz + 1] = crc & 0xFF;
ac_io_emu_icca_cipher(icca, resp.cmd.raw, 18);
}
}
ac_io_emu_response_push(icca->emu, &resp, delay_us);
}
static void ac_io_emu_icca_cipher_shift_keys(struct ac_io_emu_icca *icca)
{
uint32_t key4_old = icca->cipher_keys[3];
uint32_t key_new = (key4_old << 11) ^ key4_old;
uint32_t key1_old = icca->cipher_keys[0];
// shift keys up
icca->cipher_keys[3] = icca->cipher_keys[2];
icca->cipher_keys[2] = icca->cipher_keys[1];
icca->cipher_keys[1] = icca->cipher_keys[0];
icca->cipher_keys[0] =
((((key1_old >> 11) ^ key_new) >> 8) ^ key_new ^ key1_old);
}
static void
ac_io_emu_icca_cipher(struct ac_io_emu_icca *icca, uint8_t *data, size_t length)
{
for (size_t i = 0; i < length; i++) {
uint8_t count4 = i % 4;
// shift keys every 4 bytes
if (count4 == 0) {
ac_io_emu_icca_cipher_shift_keys(icca);
}
// process data
data[i] =
(uint8_t)(icca->cipher_keys[0] >> (((3 - count4) << 3)) ^ data[i]);
}
}
static void ac_io_emu_icca_cipher_set_key(
struct ac_io_emu_icca *icca, const struct ac_io_message *req)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = 4; // 4 bytes, uint32_t
uint32_t host_key = 0x00000000;
host_key |= req->cmd.raw[0] << 24;
host_key |= req->cmd.raw[1] << 16;
host_key |= req->cmd.raw[2] << 8;
host_key |= req->cmd.raw[3];
// TODO: should probably RNG this
uint32_t reader_key = 0x14243444;
resp.cmd.raw[0] = (reader_key >> 24) & 0xFF;
resp.cmd.raw[1] = (reader_key >> 16) & 0xFF;
resp.cmd.raw[2] = (reader_key >> 8) & 0xFF;
resp.cmd.raw[3] = (reader_key) & 0xFF;
// so I looked these constants up, this isn't actually a secure key
// generator it's actually Marsaglia's "KISS" algorithm with different
// initial states
icca->cipher_keys[0] = reader_key ^ 0x5491333;
icca->cipher_keys[1] = host_key ^ 0x1F123BB5;
icca->cipher_keys[2] = reader_key ^ 0x159A55E5;
icca->cipher_keys[3] = host_key ^ 0x75BCD15;
log_misc("keys set to: H: %08x R: %08x", host_key, reader_key);
ac_io_emu_response_push(icca->emu, &resp, 0);
icca->cipher_started = true;
}

View File

@ -6,6 +6,12 @@
#include "acioemu/emu.h"
enum ac_io_emu_icca_version {
v150 = 0x5,
v160 = 0x6,
v170 = 0x7,
};
struct ac_io_emu_icca {
struct ac_io_emu *emu;
uint8_t unit_no;
@ -20,11 +26,19 @@ struct ac_io_emu_icca {
bool keypad_started;
bool polling_started;
uint64_t time_counter_last_poll;
enum ac_io_emu_icca_version version;
bool cipher_started;
uint32_t cipher_keys[4];
};
void ac_io_emu_icca_init(
struct ac_io_emu_icca *icca, struct ac_io_emu *emu, uint8_t unit_no);
// optional, call after init to override default version
void ac_io_emu_icca_set_version(
struct ac_io_emu_icca *icca, enum ac_io_emu_icca_version version);
void ac_io_emu_icca_dispatch_request(
struct ac_io_emu_icca *icca, const struct ac_io_message *req);

View File

@ -8,7 +8,7 @@ uint8_t crc8(const void *ptr, size_t nbytes, uint8_t in)
size_t i;
size_t j;
// log_assert(ptr != NULL);
log_assert(ptr != NULL);
bytes = ptr;
crc = ~in;
@ -35,7 +35,7 @@ uint16_t crc16(const void *ptr, size_t nbytes, uint16_t in)
size_t i;
size_t j;
// log_assert(ptr != NULL);
log_assert(ptr != NULL);
bytes = ptr;
crc = ~in;
@ -55,6 +55,33 @@ uint16_t crc16(const void *ptr, size_t nbytes, uint16_t in)
return ~crc;
}
uint16_t crc16_msb(const void *ptr, size_t nbytes, uint16_t in)
{
const uint8_t *bytes;
uint16_t crc;
size_t i;
size_t j;
log_assert(ptr != NULL);
bytes = ptr;
crc = in;
for (i = 0; i < nbytes; i++) {
crc ^= bytes[i] << 8;
for (j = 0; j < 8; j++) {
if (crc & 0x8000) {
crc = (crc << 1) ^ 0x1021;
} else {
crc = crc << 1;
}
}
}
return crc;
}
uint32_t crc32(const void *ptr, size_t nbytes, uint32_t in)
{
const uint8_t *bytes;
@ -62,7 +89,7 @@ uint32_t crc32(const void *ptr, size_t nbytes, uint32_t in)
size_t i;
size_t j;
// log_assert(ptr != NULL);
log_assert(ptr != NULL);
bytes = ptr;
crc = ~in;

View File

@ -6,6 +6,7 @@
uint8_t crc8(const void *ptr, size_t nbytes, uint8_t in);
uint16_t crc16(const void *ptr, size_t nbytes, uint16_t in);
uint16_t crc16_msb(const void *ptr, size_t nbytes, uint16_t in);
uint32_t crc32(const void *ptr, size_t nbytes, uint32_t in);
#endif