Initial re-upload of spice2x-24-08-24

This commit is contained in:
2024-08-28 11:10:34 -04:00
commit caa9e02285
1181 changed files with 380065 additions and 0 deletions

85
reader/crypt.cpp Normal file
View File

@@ -0,0 +1,85 @@
#include <cstring>
#include "crypt.h"
static uint16_t CRC_TABLE[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5,
0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b,
0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c,
0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738,
0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5,
0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969,
0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03,
0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6,
0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb,
0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1,
0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c,
0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2,
0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447,
0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2,
0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827,
0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0,
0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d,
0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba,
0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
Crypt::Crypt() {
memset(this->keys, 0, sizeof(uint32_t) * 4);
}
void Crypt::set_keys(uint32_t reader_key, uint32_t game_key) {
this->keys[0] = reader_key ^ 0x5491333;
this->keys[1] = game_key ^ 0x1F123BB5;
this->keys[2] = reader_key ^ 0x159A55E5;
this->keys[3] = game_key ^ 0x75BCD15;
}
void Crypt::crypt(uint8_t *data, size_t len) {
for (size_t i = 0; i < len; i++) {
// shift keys
int low = (int) i & 3;
if (low == 0) {
uint32_t key_new = (this->keys[3] << 11) ^ this->keys[3];
this->keys[3] = this->keys[2];
this->keys[2] = this->keys[1];
this->keys[1] = this->keys[0];
this->keys[0] = ((((this->keys[0] >> 11) ^ key_new) >> 8) ^ key_new ^ this->keys[0]);
}
// process data
data[i] = (uint8_t) (this->keys[0] >> (((3 - low) << 3)) ^ data[i]);
}
}
uint16_t Crypt::crc(const uint8_t *data, size_t len) {
uint16_t crc = 0;
for (size_t x = 0; x < len; x++) {
crc = CRC_TABLE[data[x] ^ (uint8_t) (crc >> 8)] ^ (crc << 8);
}
return crc;
}

17
reader/crypt.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
class Crypt {
private:
uint32_t keys[4];
public:
Crypt();
void set_keys(uint32_t reader_key, uint32_t game_key);
void crypt(uint8_t *data, size_t len);
uint16_t crc(const uint8_t *data, size_t len);
};

51
reader/message.cpp Normal file
View File

@@ -0,0 +1,51 @@
#include "message.h"
#include "util/logging.h"
Message::Message(std::vector<uint8_t> data) {
this->data = std::move(data);
}
uint8_t Message::chk_sum() {
// initial checksum of 0
uint64_t chk = 0;
// add all bytes to checksum
for (uint8_t c : this->data)
chk += c;
// return checksum
return (uint8_t) (chk & 0xFF);
}
std::vector<uint8_t> Message::get_data_encoded() {
// check if already encoded
if (data.size() <= encoded.size())
return encoded;
// encode data
for (uint8_t c : this->data) {
if (c == 0xAA || c == 0xFF) {
encoded.push_back(0xFF);
encoded.push_back(~c);
} else {
encoded.push_back(c);
}
}
// return data
return this->encoded;
}
void Message::print_debug() {
std::ostringstream ss;
for (uint8_t b : this->data)
ss << " " << (void *) ((long long) b);
ss << " / ";
for (uint8_t b : this->get_data_encoded())
ss << " " << (void *) ((long long) b);
log_info("reader", "{}", ss.str());
}

24
reader/message.h Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <vector>
#include <cstdint>
class Message {
private:
std::vector<uint8_t> encoded;
protected:
std::vector<uint8_t> data;
inline void reencode() { encoded.clear(); }
public:
explicit Message(std::vector<uint8_t> data);
uint8_t chk_sum();
std::vector<uint8_t> get_data_encoded();
inline std::vector<uint8_t> get_data() { return data; }
void print_debug();
};

474
reader/reader.cpp Normal file
View File

@@ -0,0 +1,474 @@
#include "reader.h"
#include <filesystem>
#include <thread>
#include <cstring>
#include <vector>
#include "util/logging.h"
#include "misc/eamuse.h"
#include "util/utils.h"
#include "structuredmessage.h"
static std::vector<std::thread *> READER_THREADS;
static bool READER_THREAD_RUNNING = false;
Reader::Reader(const std::string &port) : port(port) {
// open port using an NT path to support COM ports past 9
std::filesystem::path serial_path = fmt::format("\\\\.\\{}", this->port);
this->serial_handle = CreateFileW(
serial_path.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
nullptr,
OPEN_EXISTING,
0,
nullptr);
// check if valid
this->valid = this->serial_handle != INVALID_HANDLE_VALUE;
if (!this->valid) {
auto last_error = get_last_error_string();
log_warning("reader", "{}: failed to open serial connection for reader: {}",
this->port,
last_error);
}
}
Reader::~Reader() {
if (this->serial_handle) {
CloseHandle(this->serial_handle);
log_info("reader", "closed reader on {}", this->port);
}
}
bool Reader::is_valid() {
return this->valid;
}
bool Reader::initialize() {
if (!this->set_comm_state(CBR_57600) || !this->wait_for_handshake())
return false;
log_info("reader", "{}: card reader connected", this->port);
// assign reader ID
std::vector<uint8_t> set_id_data;
set_id_data.push_back(0x00);
if (this->msg_write_read(StructuredMessage(
0,
this->reinitialized,
READER_CMD_SET_ID,
this->gen_msg_id(),
set_id_data)).empty())
return false;
// get version
std::vector<Message> ret = this->msg_write_cmd_read(READER_CMD_VERSION);
if (ret.empty())
return false;
// print version info
std::vector<uint8_t> version_data = ret[ret.size() - 1].get_data();
std::ostringstream model;
model << version_data[13] << version_data[14] << version_data[15] << version_data[16];
log_info("reader", "{}: card reader model: {}", this->port, model.str());
std::ostringstream date;
date << (const char *) &version_data[17];
log_info("reader", "{}: card reader date: {}", this->port, date.str());
std::ostringstream clock;
clock << (const char *) &version_data[33];
log_info("reader", "{}: card reader clock: {}", this->port, clock.str());
// init 2
if (this->msg_write_cmd_read(READER_CMD_INIT2).empty())
return false;
// reinitialize
this->reinitialized = 1;
std::vector<uint8_t> reinitialize_data;
reinitialize_data.push_back(0x00);
if (this->msg_write_cmd_read(READER_CMD_REINITIALIZE, reinitialize_data).empty())
return false;
log_info("reader", "{}: card reader init done", this->port);
return true;
}
bool Reader::init_crypt() {
// generate game key
std::vector<uint8_t> gk;
for (int i = 0; i < 4; i++)
gk.push_back((uint8_t) (rand() % 256));
// reader crypt init
std::vector<Message> ret = this->msg_write_cmd_read(READER_CMD_KEY_EXCHANGE, gk);
if (ret.empty())
return false;
// validate message
Message msg = ret[ret.size() - 1];
std::vector<uint8_t> md = msg.get_data();
if (md.size() != 9)
return false;
// convert keys to int32
uint32_t game_key = gk[0] << 24 | gk[1] << 16 | gk[2] << 8 | gk[3];
uint32_t reader_key = md[5] << 24 | md[6] << 16 | md[7] << 8 | md[8];
log_info("reader", "{}: reader crypt client key: {}", this->port, bin2hex((char *) &gk[0], 4));
log_info("reader", "{}: reader crypt reader key: {}", this->port, bin2hex((char *) &md[5], 4));
// set crypt keys
this->crypt.set_keys(reader_key, game_key);
// crypt done
log_info("reader", "{}: reader crypt init done", this->port);
return true;
}
bool Reader::read_card() {
// read card UID
std::vector<uint8_t> status_ruid_data;
status_ruid_data.push_back(0x00);
status_ruid_data.push_back(0x03);
status_ruid_data.push_back(0xFF);
status_ruid_data.push_back(0xFF);
if (this->msg_write_cmd_read(READER_CMD_RFID_READ_UID, status_ruid_data).empty()) {
this->valid = false;
return false;
}
Sleep(200);
// get reader status
std::vector<uint8_t> status_req_data;
status_req_data.push_back(0x10);
std::vector<Message> ret = this->msg_write_cmd_read(READER_CMD_GET_STATUS_ENC, status_req_data);
if (ret.empty()) {
this->valid = false;
return false;
}
// get data
Message status_msg = ret[ret.size() - 1];
std::vector<uint8_t> status_data = status_msg.get_data();
if (status_data.size() != 23)
return false;
// decrypt data
this->crypt.crypt(&status_data[5], 18);
// check CRC
uint16_t crc_old = status_data[21] << 8 | status_data[22];
uint16_t crc_new = this->crypt.crc(&status_data[5], 16);
if (crc_old != crc_new) {
this->valid = false;
return false;
}
// get keypad state
this->keypad_started = status_data[16];
this->keypad_state = status_data[19] << 8 | status_data[20];
// check for card input
if (status_data[5] == 2) {
memcpy(this->card_uid, &status_data[7], 8);
return true;
}
return false;
}
bool Reader::set_comm_state(DWORD BaudRate) {
// settings
DCB serial_params{};
serial_params.DCBlength = sizeof(serial_params);
if (!GetCommState(this->serial_handle, &serial_params)) {
log_warning("reader", "{}: unable to get COM port state: 0x{:x}", this->port, GetLastError());
return false;
}
serial_params.BaudRate = BaudRate;
serial_params.ByteSize = 8;
serial_params.StopBits = ONESTOPBIT;
serial_params.Parity = NOPARITY;
if (!SetCommState(this->serial_handle, &serial_params)) {
log_warning("reader", "{}: unable to set COM port state: 0x{:x}", this->port, GetLastError());
return false;
}
// timeouts
COMMTIMEOUTS timeouts{};
timeouts.ReadIntervalTimeout = 30;
timeouts.ReadTotalTimeoutConstant = 30;
timeouts.ReadTotalTimeoutMultiplier = 5;
timeouts.WriteTotalTimeoutConstant = 30;
timeouts.WriteTotalTimeoutMultiplier = 5;
if (!SetCommTimeouts(this->serial_handle, &timeouts)) {
log_warning("reader", "{}: unable to set COM port timeouts: 0x{:x}", this->port, GetLastError());
return false;
}
return true;
}
bool Reader::wait_for_handshake() {
// baud rates
DWORD baud_rates[] = { CBR_57600, CBR_38400, CBR_19200, CBR_9600 };
// variables
DWORD bytes_written = 0;
DWORD bytes_read = 0;
uint8_t read_buffer[565];
// generate handshake buffer
uint8_t handshake_buffer[565];
memset(handshake_buffer, 0, 525);
memset(handshake_buffer + 525, 0xAA, 40);
// try all the baud rates
for (size_t i = 0; i < 4; i++) {
this->set_comm_state(baud_rates[i]);
// handshake loop
for (size_t n = 0; n < 10; n++) {
// write handshake
if (!WriteFile(
this->serial_handle,
handshake_buffer,
sizeof(handshake_buffer),
&bytes_written,
nullptr))
{
break;
}
// read handshake
bytes_read = 0;
if (!ReadFile(
this->serial_handle,
read_buffer,
sizeof(read_buffer),
&bytes_read,
nullptr))
{
break;
}
// check handshake
if (bytes_read > 0 && read_buffer[bytes_read - 1] == 0xAA) {
return true;
}
// sleep
Sleep(50);
}
log_warning("reader", "{}: no handshake received for {} baud", this->port, baud_rates[i]);
}
// no handshake on all baud rates
log_warning("reader", "{}: no handshake received for any attempted baud rate", this->port);
return false;
}
bool Reader::msg_write(Message msg) {
// get message data
std::vector<uint8_t> msg_encoded = msg.get_data_encoded();
uint8_t chk_sum = msg.chk_sum();
// create write buffer
uint8_t write_buffer[512];
DWORD write_buffer_len = 0;
// fill write buffer
write_buffer[write_buffer_len++] = 0xAA;
for (const uint8_t c : msg_encoded) {
write_buffer[write_buffer_len++] = c;
}
// write checksum
if (chk_sum == 0xAA || chk_sum == 0xFF) {
write_buffer[write_buffer_len++] = 0xFF;
write_buffer[write_buffer_len++] = ~chk_sum;
} else {
write_buffer[write_buffer_len++] = chk_sum;
}
// write buffer
DWORD bytes_written = 0;
return WriteFile(this->serial_handle,
write_buffer,
write_buffer_len,
&bytes_written,
nullptr) != 0;
}
std::vector<Message> Reader::msg_write_read(Message msg) {
this->msg_write(msg);
return this->msg_read();
}
std::vector<Message> Reader::msg_write_cmd_read(uint8_t cmd) {
return this->msg_write_cmd_read(cmd, std::vector<uint8_t>());
}
std::vector<Message> Reader::msg_write_cmd_read(uint8_t cmd, std::vector<uint8_t> data) {
this->msg_write(StructuredMessage(
this->node,
this->reinitialized,
cmd,
this->gen_msg_id(),
data
));
return this->msg_read();
}
std::vector<Message> Reader::msg_read() {
// create buffer
std::vector<Message> msgs;
uint8_t read_buffer[4096];
DWORD read_buffer_len = 0;
// read to buffer
if (ReadFile(
this->serial_handle,
read_buffer,
sizeof(read_buffer),
&read_buffer_len,
nullptr) && read_buffer_len > 0)
{
std::vector<uint8_t> msg_data;
size_t msg_remaining = 0;
bool escape = false;
for (size_t i = 0; i < read_buffer_len; i++) {
uint8_t b = read_buffer[i];
if (msg_remaining > 0) {
// add msg data length
if (msg_data.size() < 6 && msg_remaining == 1)
msg_remaining += msg_data[4];
if (escape) { // escaped byte
b = ~b;
msg_data.push_back(b);
msg_remaining--;
escape = false;
} else if (b == 0xAA) { // message start
msg_remaining = 6;
msg_data.clear();
} else if (b == 0xFF) { // escape
escape = true;
} else { // normal data
msg_data.push_back(b);
msg_remaining--;
}
} else if (b == 0xAA) { // message start
msg_remaining = 6;
msg_data.clear();
}
if (msg_remaining == 0 && msg_data.size() >= 6) { // message done
std::vector<uint8_t> msg_ext_data;
for (size_t n = 0; n < msg_data[4] && n < msg_data.size() - 6; n++) {
msg_ext_data.push_back(msg_data[5 + n]);
}
StructuredMessage msg(
msg_data[0],
msg_data[1],
msg_data[2],
msg_data[3],
msg_ext_data
);
if (msg.chk_sum() == msg_data[msg_data.size() - 1]) {
msgs.push_back(msg);
}
msg_data.clear();
}
}
}
// return message buffer
return msgs;
}
void start_reader_thread(const std::string &port, int id) {
READER_THREAD_RUNNING = true;
READER_THREADS.push_back(new std::thread([port, id]() {
log_info("reader", "{}: starting reader thread", port);
while (READER_THREAD_RUNNING) {
// create reader
Reader reader(port);
// check if serial handle is still valid
if (!reader.is_valid()) {
log_warning("reader", "{}: serial handle no longer valid", port);
} else if (!reader.initialize()) {
log_warning("reader", "{}: unable to initialize reader", port);
} else if (reader.init_crypt()) {
// reader loop
while (READER_THREAD_RUNNING && reader.is_valid()) {
bool did_read_card = reader.read_card();
if (did_read_card) {
const uint8_t *uid = reader.get_card_uid();
log_info("reader", "{}: reader input: {}", port, bin2hex(uid, 8));
if (id >= 0) {
eamuse_card_insert(id, uid);
} else {
eamuse_card_insert(GetKeyState(VK_NUMLOCK) & 1, uid);
}
}
if (reader.keypad_started > 0) {
if (id >= 0) {
eamuse_set_keypad_overrides_reader(id, reader.keypad_state);
} else {
auto unit = GetKeyState(VK_NUMLOCK) & 1;
eamuse_set_keypad_overrides_reader(unit, reader.keypad_state);
}
}
if (did_read_card) {
Sleep(2500);
}
Sleep(20);
}
}
// sleep between reader connection retries
if (READER_THREAD_RUNNING)
Sleep(5000);
}
}));
// wait for thread to start
Sleep(10);
}
void stop_reader_thread() {
// stop threads
if (READER_THREAD_RUNNING) {
READER_THREAD_RUNNING = false;
}
// kill threads
while (!READER_THREADS.empty()) {
delete READER_THREADS.back();
READER_THREADS.pop_back();
}
}

72
reader/reader.h Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#include <string>
#include <windows.h>
#include "crypt.h"
#include "message.h"
enum reader_cmd {
READER_CMD_SET_ID = 0x01,
READER_CMD_VERSION = 0x02,
READER_CMD_INIT2 = 0x03,
READER_CMD_REINITIALIZE = 0x30,
READER_CMD_READ_CARD_UID = 0x31,
READER_CMD_GET_STATUS = 0x34,
READER_CMD_SET_ACTION = 0x35,
READER_CMD_SLEEP_MODE = 0x3A,
READER_CMD_KEY_EXCHANGE = 0x60,
READER_CMD_RFID_READ_UID = 0x61,
READER_CMD_GET_STATUS_ENC = 0x64,
};
enum reader_action {
READER_ACTION_BLOCK = 0x00,
READER_ACTION_ACCEPT_CARD = 0x11,
READER_ACTION_EJECT_CARD = 0x12,
};
class Reader {
public:
uint8_t keypad_started = 0;
uint16_t keypad_state = 0;
explicit Reader(const std::string &port);
~Reader();
inline const uint8_t *get_card_uid() {
return this->card_uid;
}
bool is_valid();
bool initialize();
bool init_crypt();
bool read_card();
private:
const std::string port;
HANDLE serial_handle;
bool valid;
uint8_t card_uid[8];
uint8_t reinitialized = 0, node = 1;
uint8_t cur_msg_id = 0;
Crypt crypt;
inline uint8_t gen_msg_id() { return ++cur_msg_id; }
bool set_comm_state(DWORD BaudRate);
bool wait_for_handshake();
bool msg_write(Message msg);
std::vector<Message> msg_write_read(Message msg);
std::vector<Message> msg_write_cmd_read(uint8_t cmd);
std::vector<Message> msg_write_cmd_read(uint8_t cmd, std::vector<uint8_t> data);
std::vector<Message> msg_read();
};
void start_reader_thread(const std::string &serial_str, int id);
void stop_reader_thread();

View File

@@ -0,0 +1,14 @@
#include "structuredmessage.h"
StructuredMessage::StructuredMessage(uint8_t node, uint8_t param, uint8_t cmd,
uint8_t packet_id, std::vector<uint8_t> request_data)
: Message(std::vector<uint8_t>()) {
this->data.clear();
this->data.push_back(node);
this->data.push_back(param);
this->data.push_back(cmd);
this->data.push_back(packet_id);
this->data.push_back((uint8_t) request_data.size());
this->data.insert(this->data.end(), std::begin(request_data), std::end(request_data));
this->reencode();
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "message.h"
class StructuredMessage : public Message {
public:
StructuredMessage(uint8_t node, uint8_t param, uint8_t cmd,
uint8_t packet_id, std::vector<uint8_t> request_data);
};