Compare commits

...

2 Commits

Author SHA1 Message Date
Kayori
9ac9ddb168 Merge branch 'main' of https://gitea.tendokyu.moe/Kayori/Medusa 2024-09-10 01:38:44 +02:00
Kayori
27f5da4847 added card stuff to convert to konami id 2024-09-10 01:24:10 +02:00
11 changed files with 370 additions and 7 deletions

View File

@ -6,7 +6,10 @@ edition = "2021"
[dependencies]
medusa_macros = { path = "externals/medusa_macros"}
async-trait = "0.1.82"
byteorder = "1.5.0"
cbc = "0.1.2"
chrono = "0.4.38"
des = "0.8.1"
kbinxml = { git = "https://github.com/mbilker/kbinxml-rs.git", version = "3.1.1" }
lazy_static = "1.5.0"
lz77 = "0.1.0"

View File

@ -176,6 +176,69 @@ impl GetServicesHandler {
}
services
}
fn add_pan_services<'a>(
mut services: Vec<XmlEvent<'a>>,
common_url: &'a str,
) -> Vec<XmlEvent<'a>> {
let m39_services = vec![
"local", "local2", "lobby", "slocal", "slocal2", "sglocal", "sglocal2", "lab",
"globby", "slobby", "sglobby",
];
for service in m39_services {
services.push(
XmlEvent::start_element("item")
.attr("name", service)
.attr("url", &common_url)
.into(),
);
services.push(XmlEvent::end_element().into());
}
services
}
fn add_ujk_services<'a>(
mut services: Vec<XmlEvent<'a>>,
common_url: &'a str,
) -> Vec<XmlEvent<'a>> {
let m39_services = vec![
"local", "local2", "lobby", "slocal", "slocal2", "sglocal", "sglocal2", "lab",
"globby", "slobby", "sglobby",
];
for service in m39_services {
services.push(
XmlEvent::start_element("item")
.attr("name", service)
.attr("url", &common_url)
.into(),
);
services.push(XmlEvent::end_element().into());
}
services
}
fn add_l44_services<'a>(
mut services: Vec<XmlEvent<'a>>,
common_url: &'a str,
) -> Vec<XmlEvent<'a>> {
let m39_services = vec![
"local", "local2", "lobby", "slocal", "slocal2", "sglocal", "sglocal2", "lab",
"globby", "slobby", "sglobby",
];
for service in m39_services {
services.push(
XmlEvent::start_element("item")
.attr("name", service)
.attr("url", &common_url)
.into(),
);
services.push(XmlEvent::end_element().into());
}
services
}
}
#[async_trait]
@ -201,6 +264,9 @@ impl Handler for GetServicesHandler {
Some("MDX") => GetServicesHandler::add_mdx_services(services, &common_url),
Some("M39") => GetServicesHandler::add_m39_services(services, &common_url),
Some("M32") => GetServicesHandler::add_m32_services(services, &common_url),
Some("PAN") => GetServicesHandler::add_pan_services(services, &common_url),
Some("UJK") => GetServicesHandler::add_ujk_services(services, &common_url),
Some("L44") => GetServicesHandler::add_l44_services(services, &common_url),
_ => services,
};

View File

@ -0,0 +1,98 @@
use crate::{handlers::handler::Handler, utils::card_convert::CardConvert};
use std::io::{BufReader, Cursor};
use xml::{
reader::{EventReader, XmlEvent},
EmitterConfig,
};
pub struct InquireCardManagment {
card_id: String,
}
pub struct InquireCardManagmentHandler {
module: String,
method: String,
}
impl InquireCardManagmentHandler {
pub fn new() -> Self {
InquireCardManagmentHandler {
module: "cardmng".to_string(),
method: "inquire".to_string(),
}
}
fn parse_inquire_card_managment(xml_data: &str) -> Option<InquireCardManagment> {
let reader = EventReader::new(BufReader::new(xml_data.as_bytes()));
let mut inquire_card_managment = InquireCardManagment {
card_id: String::new(),
};
for event in reader {
match event {
Ok(XmlEvent::StartElement {
name, attributes, ..
}) => {
if name.local_name == "cardmng" {
for attr in attributes {
match attr.name.local_name.as_str() {
"cardid" => inquire_card_managment.card_id = attr.value.clone(),
_ => {}
}
}
}
}
Err(e) => {
print!("Error: {}", e);
return None;
}
_ => {}
}
}
Some(inquire_card_managment)
}
}
#[async_trait]
impl Handler for InquireCardManagmentHandler {
fn module(&self) -> &str {
&self.module
}
fn method(&self) -> &str {
&self.method
}
async fn handle(&self, _model: String, body: String) -> String {
let card_managment = Self::parse_inquire_card_managment(body.as_str()).unwrap();
let mut buffer = Cursor::new(vec![]);
let mut writer = EmitterConfig::new()
.perform_indent(true)
.create_writer(&mut buffer);
writer
.write(xml::writer::XmlEvent::start_element("response"))
.unwrap();
if card_managment.card_id == "" {
writer.write(xml::writer::XmlEvent::start_element("cardmng")
.attr("status", "111")).unwrap();
writer.write(xml::writer::XmlEvent::end_element()).unwrap();
writer.write(xml::writer::XmlEvent::end_element()).unwrap();
return String::from_utf8(buffer.into_inner()).unwrap()
}
let konami_id = CardConvert::to_konami_id(card_managment.card_id);
if konami_id.is_some() {
println!("Konami id: {}", konami_id.unwrap());
}
writer.write(xml::writer::XmlEvent::end_element()).unwrap();
String::from_utf8(buffer.into_inner()).unwrap()
}
}

View File

@ -0,0 +1 @@
pub mod inquire_card_managment_handler;

View File

@ -4,4 +4,5 @@ pub mod get_messages_handler;
pub mod put_pcb_event_handler;
pub mod get_facility_handler;
pub mod ota;
pub mod ota;
pub mod card;

View File

@ -4,8 +4,6 @@ use xml::{reader::{EventReader, XmlEvent}, EmitterConfig};
use crate::handlers::handler::Handler;
struct PcbEvent {
tag: String,
src_id: String,

View File

@ -6,10 +6,10 @@ mod parsers;
mod generators;
mod handlers;
mod routes;
mod utils;
use handlers::boot::get_services_handler::GetServicesHandler;
use handlers::common::alive_pcb_tracker_handler::AlivePcbTrackerHandler;
use handlers::common::get_facility_handler::GetFacilityHandler;
use handlers::common::get_messages_handler::GetMessageHandler;
use handlers::common::ota::list_package_handler::ListPackageHandler;
use handlers::common::ota::progress_dl_status_handler::ProgressDLStatusHandler;
@ -37,7 +37,6 @@ fn register_handlers() -> Vec<Arc<dyn Handler>> {
let put_pcb_event_handler = Arc::new(PutPcbEventHandler::new());
let list_package_handler = Arc::new(ListPackageHandler::new());
let progress_dl_status_handler = Arc::new(ProgressDLStatusHandler::new());
let facility_get_handler = Arc::new(GetFacilityHandler::new());
let handlers: Vec<Arc<dyn Handler>> = vec![
get_services_handler,
@ -45,8 +44,7 @@ fn register_handlers() -> Vec<Arc<dyn Handler>> {
get_messages_handler,
put_pcb_event_handler,
list_package_handler,
progress_dl_status_handler,
facility_get_handler
progress_dl_status_handler
];
handlers

128
src/utils/card_convert.rs Normal file
View File

@ -0,0 +1,128 @@
use super::triple_des;
lazy_static! {
static ref ALPHABET: Vec<char> = "0123456789ABCDEFGHJKLMNPRSTUWXYZ".chars().collect();
}
lazy_static! {
static ref RAWKEY: Vec<u8> = {
"?I'llB2c.YouXXXeMeHaYpy!"
.chars()
.filter_map(|c| if c.is_ascii() { Some(c as u8) } else { None })
.collect()
};
}
pub struct CardConvert {}
impl CardConvert {
pub fn to_konami_id(card_id: String) -> Option<String> {
if card_id.len() != 16 {
eprintln!("Invalid UID length must be 16 characters");
return None;
}
let card_type: i32 = if card_id.starts_with("E004") {
1
} else if card_id.starts_with('0') {
2
} else {
-1
};
if card_type == -1 {
eprintln!("Invalid UID prefix");
return None;
}
let mut konami_id: Vec<u8> = (0..card_id.len() / 2)
.map(|x| u8::from_str_radix(&card_id[x * 2..x * 2 + 2], 16).unwrap())
.collect();
konami_id.reverse();
if konami_id.len() != 8 {
eprintln!("Invalid UID length. Must be 8 bytes");
return None;
}
let konami_id = triple_des::encrypt(
&konami_id,
&Self::generate_encryption_key(),
konami_id.len(),
);
if konami_id.len() != 8 {
eprintln!("Invalid UID length. Must be 8 bytes");
return None;
}
let konami_id = Self::unpack_5(&konami_id);
let mut konami_id = [&konami_id[..13], &[0, 0, 0]].concat();
if konami_id.len() != 16 {
eprintln!("Invalid unpacked ID length. Must be 16 bytes");
return None;
}
konami_id[0] ^= card_type as u8;
konami_id[13] = 1;
for i in 1..14 {
konami_id[i] ^= konami_id[i - 1];
}
konami_id[14] = card_type as u8;
konami_id[15] = Self::calculate_checksum(konami_id.clone());
Some(konami_id.iter().map(|&byte| ALPHABET[byte as usize]).collect())
}
fn generate_encryption_key() -> Vec<u8> {
RAWKEY.iter().map(|&x| x * 2).collect()
}
fn unpack_5(data: &[u8]) -> Vec<u8> {
// Convert each byte to an 8-bit binary string and concatenate them
let binary_string: String = data.iter().map(|&b| format!("{:08b}", b)).collect();
// Ensure the total length is a multiple of 5 by padding with zeroes if necessary
let remainder = binary_string.len() % 5;
let padded_binary_string = if remainder != 0 {
let padding_length = 5 - remainder;
format!("{}{}", binary_string, "0".repeat(padding_length))
} else {
binary_string
};
// Convert the 5-bit chunks back into bytes
let mut result = Vec::new();
for chunk in padded_binary_string.as_bytes().chunks(5) {
let chunk_str = std::str::from_utf8(chunk).unwrap();
let byte = u8::from_str_radix(chunk_str, 2).unwrap();
result.push(byte);
}
result
}
fn calculate_checksum(konami_id: Vec<u8>) -> u8 {
let mut checksum: u8 = 0;
for i in 0..15 {
if i >= konami_id.len() {
break;
}
checksum = checksum.wrapping_add(konami_id[i] * ((i % 3) + 1) as u8);
}
while checksum > 31 {
checksum = (checksum >> 5) + (checksum & 31);
}
checksum as u8
}
}

3
src/utils/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod card_convert;
pub mod triple_des;

46
src/utils/triple_des.rs Normal file
View File

@ -0,0 +1,46 @@
use des::TdesEde3;
use cbc::cipher::{KeyIvInit, BlockEncryptMut, BlockDecryptMut};
// Alias for Triple DES CBC encryption
type TdesCbc = cbc::Encryptor<TdesEde3>;
// Alias for Triple DES CBC decryption
type TdesCbcDecryptor = cbc::Decryptor<TdesEde3>;
// Function to encrypt the data using Triple DES in CBC mode with no padding
pub fn encrypt(data: &[u8], key: &[u8], msg_size: usize) -> Vec<u8> {
// All-zero IV (8 bytes)
let iv = [0u8; 8];
// Create the Triple DES CBC encryptor with the key and IV
let cipher = TdesCbc::new(key.into(), &iv.into());
// Since padding is set to None, the input must be a multiple of the block size (8 bytes for DES/3DES)
assert!(data.len() % 8 == 0, "Input data must be a multiple of 8 bytes");
// Create a mutable buffer from the input data
let mut buffer = data.to_vec();
// Encrypt the data in place
cipher.encrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(&mut buffer, msg_size)
.expect("Encryption failed").to_vec()
}
// Function to decrypt the data using Triple DES in CBC mode with no padding
pub fn decrypt(data: &[u8], key: &[u8]) -> Vec<u8> {
// All-zero IV (8 bytes)
let iv = [0u8; 8];
// Create the Triple DES CBC decryptor with the key and IV
let mut cipher = TdesCbcDecryptor::new(key.into(), &iv.into());
// Since padding is set to None, the input must be a multiple of the block size (8 bytes for DES/3DES)
assert!(data.len() % 8 == 0, "Input data must be a multiple of 8 bytes");
// Create a mutable buffer from the input data
let mut buffer = data.to_vec();
// Decrypt the data in place
cipher.decrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(&mut buffer)
.expect("Decryption failed").to_vec()
}

View File

@ -0,0 +1,21 @@
// vite.config.ts
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "file:///D:/ProjectsOld/Rust/Medusa/web/node_modules/vite/dist/node/index.js";
import vue from "file:///D:/ProjectsOld/Rust/Medusa/web/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import vueDevTools from "file:///D:/ProjectsOld/Rust/Medusa/web/node_modules/vite-plugin-vue-devtools/dist/vite.mjs";
var __vite_injected_original_import_meta_url = "file:///D:/ProjectsOld/Rust/Medusa/web/vite.config.ts";
var vite_config_default = defineConfig({
plugins: [
vue(),
vueDevTools()
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", __vite_injected_original_import_meta_url))
}
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJEOlxcXFxQcm9qZWN0c09sZFxcXFxSdXN0XFxcXE1lZHVzYVxcXFx3ZWJcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIkQ6XFxcXFByb2plY3RzT2xkXFxcXFJ1c3RcXFxcTWVkdXNhXFxcXHdlYlxcXFx2aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vRDovUHJvamVjdHNPbGQvUnVzdC9NZWR1c2Evd2ViL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgZmlsZVVSTFRvUGF0aCwgVVJMIH0gZnJvbSAnbm9kZTp1cmwnXHJcblxyXG5pbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJ1xyXG5pbXBvcnQgdnVlIGZyb20gJ0B2aXRlanMvcGx1Z2luLXZ1ZSdcclxuaW1wb3J0IHZ1ZURldlRvb2xzIGZyb20gJ3ZpdGUtcGx1Z2luLXZ1ZS1kZXZ0b29scydcclxuXHJcbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXHJcbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XHJcbiAgcGx1Z2luczogW1xyXG4gICAgdnVlKCksXHJcbiAgICB2dWVEZXZUb29scygpLFxyXG4gIF0sXHJcbiAgcmVzb2x2ZToge1xyXG4gICAgYWxpYXM6IHtcclxuICAgICAgJ0AnOiBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4vc3JjJywgaW1wb3J0Lm1ldGEudXJsKSlcclxuICAgIH1cclxuICB9XHJcbn0pXHJcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBc1IsU0FBUyxlQUFlLFdBQVc7QUFFelQsU0FBUyxvQkFBb0I7QUFDN0IsT0FBTyxTQUFTO0FBQ2hCLE9BQU8saUJBQWlCO0FBSnFKLElBQU0sMkNBQTJDO0FBTzlOLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVM7QUFBQSxJQUNQLElBQUk7QUFBQSxJQUNKLFlBQVk7QUFBQSxFQUNkO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUCxPQUFPO0FBQUEsTUFDTCxLQUFLLGNBQWMsSUFBSSxJQUFJLFNBQVMsd0NBQWUsQ0FBQztBQUFBLElBQ3REO0FBQUEsRUFDRjtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==