Compare commits
2 Commits
21e57c481a
...
9ac9ddb168
Author | SHA1 | Date | |
---|---|---|---|
|
9ac9ddb168 | ||
|
27f5da4847 |
@ -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"
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
98
src/handlers/common/card/inquire_card_managment_handler.rs
Normal file
98
src/handlers/common/card/inquire_card_managment_handler.rs
Normal 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()
|
||||
}
|
||||
}
|
1
src/handlers/common/card/mod.rs
Normal file
1
src/handlers/common/card/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod inquire_card_managment_handler;
|
@ -5,3 +5,4 @@ pub mod put_pcb_event_handler;
|
||||
pub mod get_facility_handler;
|
||||
|
||||
pub mod ota;
|
||||
pub mod card;
|
@ -4,8 +4,6 @@ use xml::{reader::{EventReader, XmlEvent}, EmitterConfig};
|
||||
|
||||
use crate::handlers::handler::Handler;
|
||||
|
||||
|
||||
|
||||
struct PcbEvent {
|
||||
tag: String,
|
||||
src_id: String,
|
||||
|
@ -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
128
src/utils/card_convert.rs
Normal 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
3
src/utils/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
pub mod card_convert;
|
||||
pub mod triple_des;
|
46
src/utils/triple_des.rs
Normal file
46
src/utils/triple_des.rs
Normal 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()
|
||||
}
|
21
web/vite.config.ts.timestamp-1725920055853-df6655ad6213d.mjs
Normal file
21
web/vite.config.ts.timestamp-1725920055853-df6655ad6213d.mjs
Normal 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==
|
Loading…
Reference in New Issue
Block a user