feat(auth): finish protocol 10 method 2 and 3
This commit is contained in:
parent
8a3f443d85
commit
d18ba43aed
24 changed files with 490 additions and 43 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -549,6 +549,12 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
|
@ -1304,6 +1310,7 @@ dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
"log",
|
"log",
|
||||||
"md-5",
|
"md-5",
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net", "s
|
||||||
tokio-stream = { version = "0.1.17", features = ["io-util"] }
|
tokio-stream = { version = "0.1.17", features = ["io-util"] }
|
||||||
tonic = "0.12.3"
|
tonic = "0.12.3"
|
||||||
prost = "0.13.4"
|
prost = "0.13.4"
|
||||||
|
hex = "0.4.3"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.12.3"
|
tonic-build = "0.12.3"
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
use std::{env, result};
|
use std::{env, result};
|
||||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
use std::array::TryFromSliceError;
|
||||||
|
use std::net::{Ipv4Addr};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tonic::codegen::http::uri::InvalidUri;
|
|
||||||
use tonic::metadata::{Ascii, MetadataValue};
|
use tonic::metadata::{Ascii, MetadataValue};
|
||||||
use tonic::{Request, Status, transport};
|
use tonic::{Request, transport};
|
||||||
use tonic::codegen::InterceptedService;
|
use tonic::codegen::InterceptedService;
|
||||||
use tonic::service::Interceptor;
|
|
||||||
use tonic::transport::Channel;
|
use tonic::transport::Channel;
|
||||||
use crate::grpc::{InterceptorFunc, protobufs};
|
use crate::grpc::InterceptorFunc;
|
||||||
use crate::grpc::protobufs::account::account_client::AccountClient;
|
use crate::grpc::protobufs::account::account_client::AccountClient;
|
||||||
use crate::grpc::protobufs::account::GetNexPasswordRequest;
|
use crate::grpc::protobufs::account::GetNexPasswordRequest;
|
||||||
|
|
||||||
|
|
@ -42,7 +41,9 @@ pub enum Error{
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Transport(#[from] transport::Error),
|
Transport(#[from] transport::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Status(#[from] tonic::Status)
|
Status(#[from] tonic::Status),
|
||||||
|
#[error("invalid password size: {0}")]
|
||||||
|
PasswordConversion(#[from] TryFromSliceError)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
@ -64,14 +65,14 @@ impl Client{
|
||||||
Ok(Self(client))
|
Ok(Self(client))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_nex_password(&mut self , pid: u32) -> Result<Box<str>>{
|
pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{
|
||||||
let req = Request::new(GetNexPasswordRequest{
|
let req = Request::new(GetNexPasswordRequest{
|
||||||
pid
|
pid
|
||||||
});
|
});
|
||||||
|
|
||||||
let response = self.0.get_nex_password(req).await?.into_inner();
|
let response = self.0.get_nex_password(req).await?.into_inner();
|
||||||
|
|
||||||
Ok(response.password.into_boxed_str())
|
Ok(response.password.as_bytes().try_into()?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
use std::env;
|
|
||||||
use std::net::Ipv4Addr;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use tonic::{Request, Status};
|
use tonic::{Request, Status};
|
||||||
|
|
||||||
type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send)>;
|
type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send)>;
|
||||||
|
|
|
||||||
111
src/kerberos/mod.rs
Normal file
111
src/kerberos/mod.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||||
|
use chrono::{Datelike, Timelike};
|
||||||
|
use hmac::Hmac;
|
||||||
|
use md5::{Digest, Md5};
|
||||||
|
use rc4::{Rc4, Rc4Core, StreamCipher};
|
||||||
|
use rc4::cipher::StreamCipherCoreWrapper;
|
||||||
|
use rc4::consts::U16;
|
||||||
|
use hmac::Mac;
|
||||||
|
use rc4::KeyInit;
|
||||||
|
use crate::rmc::structures::RmcSerialize;
|
||||||
|
|
||||||
|
type Md5Hmac = Hmac<md5::Md5>;
|
||||||
|
|
||||||
|
pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{
|
||||||
|
let iteration_count = 65000 + pid%1024;
|
||||||
|
|
||||||
|
let mut key = password;
|
||||||
|
|
||||||
|
for _ in 0..iteration_count {
|
||||||
|
let mut md5 = Md5::new();
|
||||||
|
md5.update(key);
|
||||||
|
key = md5.finalize().try_into().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
key
|
||||||
|
}
|
||||||
|
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct KerberosDateTime(u64);
|
||||||
|
|
||||||
|
impl KerberosDateTime{
|
||||||
|
pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year:u64 ) -> Self {
|
||||||
|
Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn now() -> Self{
|
||||||
|
let now = chrono::Utc::now();
|
||||||
|
Self::new(
|
||||||
|
now.second() as u64,
|
||||||
|
now.minute() as u64,
|
||||||
|
now.hour() as u64,
|
||||||
|
now.day() as u64,
|
||||||
|
now.month() as u64,
|
||||||
|
now.year() as u64,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct TicketInternalData{
|
||||||
|
issued_time: KerberosDateTime,
|
||||||
|
pub pid: u32,
|
||||||
|
pub session_key: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TicketInternalData{
|
||||||
|
pub(crate) fn new(pid: u32) -> Self{
|
||||||
|
Self{
|
||||||
|
issued_time: KerberosDateTime::now(),
|
||||||
|
pid,
|
||||||
|
session_key: rand::random()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]>{
|
||||||
|
let mut data = bytes_of(self).to_vec();
|
||||||
|
|
||||||
|
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
|
||||||
|
rc4.apply_keystream(&mut data);
|
||||||
|
|
||||||
|
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
|
||||||
|
|
||||||
|
hmac.write_all(&data[..]).expect("failed to write data to hmac");
|
||||||
|
|
||||||
|
let hmac_result = &hmac.finalize().into_bytes()[..];
|
||||||
|
|
||||||
|
data.write_all(&hmac_result).expect("failed to write data to vec");
|
||||||
|
|
||||||
|
data.into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Ticket{
|
||||||
|
pub session_key: [u8; 32],
|
||||||
|
pub pid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ticket{
|
||||||
|
pub(crate) fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]>{
|
||||||
|
let mut data = bytes_of(self).to_vec();
|
||||||
|
|
||||||
|
internal_data.serialize(&mut data).expect("unable to write to vec");
|
||||||
|
|
||||||
|
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
|
||||||
|
rc4.apply_keystream(&mut data);
|
||||||
|
|
||||||
|
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
|
||||||
|
|
||||||
|
hmac.write_all(&data[..]).expect("failed to write data to hmac");
|
||||||
|
|
||||||
|
let hmac_result = &hmac.finalize().into_bytes()[..];
|
||||||
|
|
||||||
|
data.write_all(&hmac_result).expect("failed to write data to vec");
|
||||||
|
|
||||||
|
data.into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/main.rs
13
src/main.rs
|
|
@ -11,6 +11,7 @@ use rc4::consts::U5;
|
||||||
use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger};
|
use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger};
|
||||||
use crate::nex::account::Account;
|
use crate::nex::account::Account;
|
||||||
use crate::protocols::auth;
|
use crate::protocols::auth;
|
||||||
|
use crate::protocols::auth::AuthProtocolConfig;
|
||||||
use crate::protocols::server::RMCProtocolServer;
|
use crate::protocols::server::RMCProtocolServer;
|
||||||
use crate::prudp::socket::Socket;
|
use crate::prudp::socket::Socket;
|
||||||
use crate::prudp::packet::{PRUDPPacket, VirtualPort};
|
use crate::prudp::packet::{PRUDPPacket, VirtualPort};
|
||||||
|
|
@ -24,9 +25,10 @@ mod protocols;
|
||||||
|
|
||||||
mod nex;
|
mod nex;
|
||||||
mod grpc;
|
mod grpc;
|
||||||
|
mod kerberos;
|
||||||
|
|
||||||
static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(||{
|
static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(||{
|
||||||
env::var("AUTH_SERVER_PORT")
|
env::var("AUTH_SERVER_PASSWORD")
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap_or("password".to_owned())
|
.unwrap_or("password".to_owned())
|
||||||
});
|
});
|
||||||
|
|
@ -80,8 +82,15 @@ async fn start_servers(){
|
||||||
info!("setting up endpoints");
|
info!("setting up endpoints");
|
||||||
|
|
||||||
// dont assign it to the name _ as that will make it drop right here and now
|
// dont assign it to the name _ as that will make it drop right here and now
|
||||||
|
|
||||||
|
let auth_protocol_config = AuthProtocolConfig{
|
||||||
|
secure_server_account: &SECURE_SERVER_ACCOUNT,
|
||||||
|
build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0",
|
||||||
|
station_url: "prudps:/PID=2;sid=1;stream=10;type=2;address=31.220.75.208;port=10001;CID=1"
|
||||||
|
};
|
||||||
|
|
||||||
let rmcserver = RMCProtocolServer::new(Box::new([
|
let rmcserver = RMCProtocolServer::new(Box::new([
|
||||||
Box::new(auth::bound_protocol(&SECURE_SERVER_ACCOUNT))
|
Box::new(auth::bound_protocol(auth_protocol_config))
|
||||||
]));
|
]));
|
||||||
|
|
||||||
let mut _socket =
|
let mut _socket =
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,38 @@
|
||||||
|
|
||||||
|
|
||||||
pub struct Account{
|
pub struct Account{
|
||||||
pid: u32,
|
pub pid: u32,
|
||||||
username: Box<str>,
|
pub username: Box<str>,
|
||||||
kerbros_password: Box<str>,
|
pub kerbros_password: [u8; 16],
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Account{
|
impl Account{
|
||||||
pub fn new(pid: u32, username: &str, passwd: &str) -> Self{
|
pub fn new(pid: u32, username: &str, passwd: &str) -> Self{
|
||||||
|
let passwd_data = passwd.as_bytes();
|
||||||
|
|
||||||
|
let mut passwd = [0u8; 16];
|
||||||
|
|
||||||
|
for (idx, byte) in passwd_data.iter().enumerate(){
|
||||||
|
passwd[idx] = *byte;
|
||||||
|
}
|
||||||
|
|
||||||
Self{
|
Self{
|
||||||
kerbros_password: passwd.into(),
|
kerbros_password: passwd,
|
||||||
username: username.into(),
|
username: username.into(),
|
||||||
pid
|
pid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_raw_password(pid: u32, username: &str, passwd: [u8; 16]) -> Self{
|
||||||
|
Self{
|
||||||
|
kerbros_password: passwd,
|
||||||
|
username: username.into(),
|
||||||
|
pid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_login_data(&self) -> (u32, [u8; 16]){
|
||||||
|
(self.pid, self.kerbros_password)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use log::error;
|
use log::error;
|
||||||
use crate::nex::account::Account;
|
use crate::nex::account::Account;
|
||||||
use crate::protocols::auth::method_login_ex::login_ex;
|
use crate::protocols::auth::AuthProtocolConfig;
|
||||||
use crate::rmc::message::RMCMessage;
|
use crate::rmc::message::RMCMessage;
|
||||||
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
||||||
use crate::rmc::structures::any::Any;
|
|
||||||
use crate::rmc::structures::RmcSerialize;
|
use crate::rmc::structures::RmcSerialize;
|
||||||
|
|
||||||
pub async fn login(rmcmessage: &RMCMessage, name: &str) -> RMCResponseResult{
|
pub async fn login(rmcmessage: &RMCMessage, _name: &str) -> RMCResponseResult{
|
||||||
|
|
||||||
|
|
||||||
rmcmessage.error_result_with_code(ErrorCode::Core_NotImplemented)
|
rmcmessage.error_result_with_code(ErrorCode::Core_NotImplemented)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn login_raw_params(rmcmessage: &RMCMessage, data: (&Account)) -> RMCResponseResult{
|
pub async fn login_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{
|
||||||
let mut reader = Cursor::new(&rmcmessage.rest_of_data);
|
let mut reader = Cursor::new(&rmcmessage.rest_of_data);
|
||||||
|
|
||||||
let Ok(str) = String::deserialize(&mut reader) else {
|
let Ok(str) = String::deserialize(&mut reader) else {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
use std::io::Cursor;
|
use std::io::{Cursor, Write};
|
||||||
use log::{error, info};
|
use bytemuck::bytes_of;
|
||||||
|
use hex::encode;
|
||||||
|
use log::{error};
|
||||||
use crate::grpc::account;
|
use crate::grpc::account;
|
||||||
|
use crate::kerberos::KerberosDateTime;
|
||||||
use crate::nex::account::Account;
|
use crate::nex::account::Account;
|
||||||
|
use crate::protocols::auth::AuthProtocolConfig;
|
||||||
|
use crate::protocols::auth::ticket_generation::generate_ticket;
|
||||||
use crate::rmc::message::RMCMessage;
|
use crate::rmc::message::RMCMessage;
|
||||||
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
||||||
use crate::rmc::structures::{RmcSerialize};
|
use crate::rmc::structures::{RmcSerialize};
|
||||||
use crate::rmc::structures::any::Any;
|
use crate::rmc::structures::any::Any;
|
||||||
|
use crate::rmc::structures::connection_data::ConnectionData;
|
||||||
|
use crate::rmc::structures::qresult::QResult;
|
||||||
|
|
||||||
pub async fn login_ex(rmcmessage: &RMCMessage, secure_server_account: &Account , pid: u32) -> RMCResponseResult{
|
pub async fn login_ex(rmcmessage: &RMCMessage, proto_data: AuthProtocolConfig, pid: u32) -> RMCResponseResult{
|
||||||
// todo: figure out how the AuthenticationInfo struct works, parse it and validate login info
|
// todo: figure out how the AuthenticationInfo struct works, parse it and validate login info
|
||||||
|
|
||||||
let Ok(mut client) = account::Client::new().await else {
|
let Ok(mut client) = account::Client::new().await else {
|
||||||
|
|
@ -18,15 +25,35 @@ pub async fn login_ex(rmcmessage: &RMCMessage, secure_server_account: &Account ,
|
||||||
return rmcmessage.error_result_with_code(ErrorCode::Core_Exception);
|
return rmcmessage.error_result_with_code(ErrorCode::Core_Exception);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let source_login_data = (pid, passwd);
|
||||||
|
let destination_login_data = proto_data.secure_server_account.get_login_data();
|
||||||
|
|
||||||
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
|
let ticket = generate_ticket(source_login_data, destination_login_data);
|
||||||
|
|
||||||
|
let result = QResult::success(ErrorCode::Core_Unknown);
|
||||||
|
|
||||||
|
let connection_data = ConnectionData{
|
||||||
|
station_url: proto_data.station_url,
|
||||||
|
special_station_url: "",
|
||||||
|
date_time: KerberosDateTime::now(),
|
||||||
|
special_protocols: Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut response: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
result.serialize(&mut response).expect("failed serializing result");
|
||||||
|
response.write_all(bytes_of(&source_login_data.0)).expect("failed writing pid");
|
||||||
|
ticket.serialize(&mut response).expect("failed serializing ticket");
|
||||||
|
connection_data.serialize(&mut response).expect("failed writing connection data");
|
||||||
|
proto_data.build_name.serialize(&mut response).expect("failed writing build name");
|
||||||
|
|
||||||
|
return rmcmessage.success_with_data(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn login_ex_raw_params(rmcmessage: &RMCMessage, (secure_server_account): (&Account)) -> RMCResponseResult{
|
pub async fn login_ex_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{
|
||||||
let mut reader = Cursor::new(&rmcmessage.rest_of_data);
|
let mut reader = Cursor::new(&rmcmessage.rest_of_data);
|
||||||
|
|
||||||
let Ok(_str) = String::deserialize(&mut reader) else {
|
let Ok(str) = String::deserialize(&mut reader) else {
|
||||||
error!("error reading packet");
|
error!("error reading packet");
|
||||||
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
|
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
|
||||||
};
|
};
|
||||||
|
|
@ -50,5 +77,5 @@ pub async fn login_ex_raw_params(rmcmessage: &RMCMessage, (secure_server_account
|
||||||
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
|
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
|
||||||
};
|
};
|
||||||
|
|
||||||
login_ex(rmcmessage, secure_server_account, pid).await
|
login_ex(rmcmessage, data, pid).await
|
||||||
}
|
}
|
||||||
53
src/protocols/auth/method_request_ticket.rs
Normal file
53
src/protocols/auth/method_request_ticket.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
use std::io::Cursor;
|
||||||
|
use log::error;
|
||||||
|
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
||||||
|
use crate::grpc::account;
|
||||||
|
use crate::protocols::auth::{AuthProtocolConfig, get_login_data_by_pid};
|
||||||
|
use crate::protocols::auth::method_login_ex::login_ex;
|
||||||
|
use crate::protocols::auth::ticket_generation::generate_ticket;
|
||||||
|
use crate::rmc::message::RMCMessage;
|
||||||
|
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
||||||
|
use crate::rmc::response::ErrorCode::Core_Unknown;
|
||||||
|
use crate::rmc::structures::any::Any;
|
||||||
|
use crate::rmc::structures::qresult::QResult;
|
||||||
|
use crate::rmc::structures::RmcSerialize;
|
||||||
|
|
||||||
|
pub async fn request_ticket(rmcmessage: &RMCMessage, data: AuthProtocolConfig, source_pid: u32, destination_pid: u32) -> RMCResponseResult{
|
||||||
|
let Some(source_login_data) = get_login_data_by_pid(source_pid).await else {
|
||||||
|
return rmcmessage.error_result_with_code(ErrorCode::Core_Exception);
|
||||||
|
};
|
||||||
|
|
||||||
|
let desgination_login_data = if destination_pid == data.secure_server_account.pid{
|
||||||
|
data.secure_server_account.get_login_data()
|
||||||
|
} else {
|
||||||
|
let Some(login) = get_login_data_by_pid(destination_pid).await else {
|
||||||
|
return rmcmessage.error_result_with_code(ErrorCode::Core_Exception);
|
||||||
|
};
|
||||||
|
login
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = QResult::success(Core_Unknown);
|
||||||
|
|
||||||
|
let ticket = generate_ticket(source_login_data, desgination_login_data);
|
||||||
|
|
||||||
|
let mut response: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
result.serialize(&mut response).expect("failed serializing result");
|
||||||
|
ticket.serialize(&mut response).expect("failed serializing ticket");
|
||||||
|
|
||||||
|
rmcmessage.success_with_data(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn request_ticket_raw_params(rmcmessage: &RMCMessage, data: AuthProtocolConfig) -> RMCResponseResult{
|
||||||
|
let mut reader = Cursor::new(&rmcmessage.rest_of_data);
|
||||||
|
|
||||||
|
let Ok(source_pid) = reader.read_struct(IS_BIG_ENDIAN) else {
|
||||||
|
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(destination_pid) = reader.read_struct(IS_BIG_ENDIAN) else {
|
||||||
|
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
|
||||||
|
};
|
||||||
|
|
||||||
|
request_ticket(rmcmessage, data, source_pid, destination_pid).await
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,42 @@
|
||||||
mod method_login_ex;
|
mod method_login_ex;
|
||||||
mod method_login;
|
mod method_login;
|
||||||
|
mod ticket_generation;
|
||||||
|
mod method_request_ticket;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
use log::{error};
|
use log::{error};
|
||||||
use crate::define_protocol;
|
use crate::define_protocol;
|
||||||
|
use crate::grpc::account;
|
||||||
use crate::nex::account::Account;
|
use crate::nex::account::Account;
|
||||||
use crate::protocols::auth::method_login::login_raw_params;
|
use crate::protocols::auth::method_login::login_raw_params;
|
||||||
use crate::protocols::auth::method_login_ex::{login_ex, login_ex_raw_params};
|
use crate::protocols::auth::method_login_ex::login_ex_raw_params;
|
||||||
|
use crate::protocols::auth::method_request_ticket::request_ticket_raw_params;
|
||||||
use crate::rmc::message::RMCMessage;
|
use crate::rmc::message::RMCMessage;
|
||||||
use crate::rmc::response::{ErrorCode, RMCResponse};
|
use crate::rmc::response::{ErrorCode, RMCResponse};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct AuthProtocolConfig {
|
||||||
|
pub secure_server_account: &'static Account,
|
||||||
|
pub build_name: &'static str,
|
||||||
|
pub station_url: &'static str
|
||||||
|
}
|
||||||
|
|
||||||
define_protocol!{
|
define_protocol!{
|
||||||
10(secure_server_account: &'static Account) => {
|
10(proto_data: AuthProtocolConfig) => {
|
||||||
0x01 => login_raw_params,
|
0x01 => login_raw_params,
|
||||||
0x02 => login_ex_raw_params
|
0x02 => login_ex_raw_params,
|
||||||
|
0x03 => request_ticket_raw_params
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> {
|
||||||
|
let Ok(mut client) = account::Client::new().await else {
|
||||||
|
return None
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(passwd) = client.get_nex_password(pid).await else{
|
||||||
|
return None
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((pid, passwd))
|
||||||
|
}
|
||||||
19
src/protocols/auth/ticket_generation.rs
Normal file
19
src/protocols/auth/ticket_generation.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::kerberos;
|
||||||
|
use crate::kerberos::{derive_key, Ticket};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn generate_ticket(source_act_login_data: (u32, [u8;16]), dest_act_login_data: (u32, [u8;16])) -> Box<[u8]>{
|
||||||
|
let source_key = derive_key(source_act_login_data.0, source_act_login_data.1);
|
||||||
|
let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1);
|
||||||
|
|
||||||
|
let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0);
|
||||||
|
|
||||||
|
let encrypted_inner = internal_data.encrypt(dest_key);
|
||||||
|
let encrypted_session_ticket = Ticket{
|
||||||
|
pid: dest_act_login_data.0,
|
||||||
|
session_key: internal_data.session_key,
|
||||||
|
}.encrypt(source_key, &encrypted_inner);
|
||||||
|
|
||||||
|
|
||||||
|
encrypted_session_ticket
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ pub mod server;
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! define_protocol {
|
macro_rules! define_protocol {
|
||||||
($id:literal ($($varname:ident : $ty:ty),*) => {$($func_id:literal => $func:path),*} ) => {
|
($id:literal ($($varname:ident : $ty:ty),*) => {$($func_id:literal => $func:path),*} ) => {
|
||||||
|
#[allow(unused_parens)]
|
||||||
async fn protocol (rmcmessage: &RMCMessage, $($varname : $ty),*) -> Option<RMCResponse>{
|
async fn protocol (rmcmessage: &RMCMessage, $($varname : $ty),*) -> Option<RMCResponse>{
|
||||||
if rmcmessage.protocol_id != $id{
|
if rmcmessage.protocol_id != $id{
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -30,7 +31,7 @@ macro_rules! define_protocol {
|
||||||
response_result
|
response_result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
#[allow(unused_parens)]
|
||||||
pub fn bound_protocol($($varname : $ty,)*) -> Box<dyn for<'message_lifetime> Fn(&'message_lifetime RMCMessage) -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = Option<RMCResponse>> + Send + 'message_lifetime>> + Send + Sync>{
|
pub fn bound_protocol($($varname : $ty,)*) -> Box<dyn for<'message_lifetime> Fn(&'message_lifetime RMCMessage) -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = Option<RMCResponse>> + Send + 'message_lifetime>> + Send + Sync>{
|
||||||
Box::new(
|
Box::new(
|
||||||
move |v| {
|
move |v| {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use hmac::{Hmac, Mac};
|
||||||
use log::{error, trace, warn};
|
use log::{error, trace, warn};
|
||||||
use md5::{Md5, Digest};
|
use md5::{Md5, Digest};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use v_byte_macros::{EnumTryInto, SwapEndian};
|
use v_byte_macros::{SwapEndian};
|
||||||
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
||||||
use crate::prudp::packet::flags::ACK;
|
use crate::prudp::packet::flags::ACK;
|
||||||
use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions};
|
use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions};
|
||||||
|
|
@ -173,13 +173,6 @@ impl Default for PRUDPHeader{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(EnumTryInto)]
|
|
||||||
|
|
||||||
#[repr(u16)]
|
|
||||||
enum PacketSpecificData {
|
|
||||||
E = 0x10
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum PacketOption{
|
pub enum PacketOption{
|
||||||
SupportedFunctions(u32),
|
SupportedFunctions(u32),
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ pub struct ActiveConnectionData {
|
||||||
pub reliable_client_counter: u16,
|
pub reliable_client_counter: u16,
|
||||||
pub reliable_server_counter: u16,
|
pub reliable_server_counter: u16,
|
||||||
pub reliable_client_queue: VecDeque<PRUDPPacket>,
|
pub reliable_client_queue: VecDeque<PRUDPPacket>,
|
||||||
pub connection_data_channel: Sender<Vec<u8>>,
|
|
||||||
server_encryption: Box<dyn StreamCipher + Send>,
|
server_encryption: Box<dyn StreamCipher + Send>,
|
||||||
client_decryption: Box<dyn StreamCipher + Send>,
|
client_decryption: Box<dyn StreamCipher + Send>,
|
||||||
pub server_session_id: u8,
|
pub server_session_id: u8,
|
||||||
|
|
@ -363,7 +362,7 @@ impl SocketData {
|
||||||
self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back");
|
self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3 => {}
|
||||||
_ => unimplemented!("unimplemented packet type: {}", packet.header.types_and_flags.get_types())
|
_ => unimplemented!("unimplemented packet type: {}", packet.header.types_and_flags.get_types())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,4 +58,12 @@ impl RMCMessage{
|
||||||
error_code
|
error_code
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn success_with_data(&self, data: Vec<u8>) -> RMCResponseResult{
|
||||||
|
RMCResponseResult::Success {
|
||||||
|
call_id: self.call_id,
|
||||||
|
method_id: self.method_id,
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ use crate::prudp::packet::PacketOption::FragmentId;
|
||||||
use crate::prudp::packet::types::DATA;
|
use crate::prudp::packet::types::DATA;
|
||||||
use crate::prudp::socket::{ConnectionData, SocketData};
|
use crate::prudp::socket::{ConnectionData, SocketData};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub enum RMCResponseResult {
|
pub enum RMCResponseResult {
|
||||||
Success{
|
Success{
|
||||||
call_id: u32,
|
call_id: u32,
|
||||||
|
|
@ -389,6 +391,7 @@ mod test{
|
||||||
use hmac::digest::consts::U5;
|
use hmac::digest::consts::U5;
|
||||||
use hmac::digest::KeyInit;
|
use hmac::digest::KeyInit;
|
||||||
use rc4::{Rc4, StreamCipher};
|
use rc4::{Rc4, StreamCipher};
|
||||||
|
use crate::rmc::response::ErrorCode;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test(){
|
fn test(){
|
||||||
|
|
@ -410,4 +413,10 @@ mod test{
|
||||||
assert_eq!(data_orig, data);
|
assert_eq!(data_orig, data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_enum_equivilance(){
|
||||||
|
let val: u32 = ErrorCode::Core_Unknown.into();
|
||||||
|
assert_eq!(val, 0x00010001)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
44
src/rmc/structures/buffer.rs
Normal file
44
src/rmc/structures/buffer.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
||||||
|
use crate::rmc::structures::RmcSerialize;
|
||||||
|
|
||||||
|
impl<'a> RmcSerialize for &'a [u8]{
|
||||||
|
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||||
|
let u32_size = self.len() as u32;
|
||||||
|
writer.write(bytemuck::bytes_of(&u32_size))?;
|
||||||
|
writer.write(self)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DO NOT USE (also maybe split off the serialize and deserialize functions at some point)
|
||||||
|
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||||
|
panic!("cannot deserialize to a u8 slice reference (use this ONLY for writing)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RmcSerialize for Vec<u8>{
|
||||||
|
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||||
|
(&self[..]).serialize(writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||||
|
let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||||
|
|
||||||
|
let mut data = vec![0; len as usize];
|
||||||
|
|
||||||
|
reader.read_exact(&mut data)?;
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RmcSerialize for Box<[u8]>{
|
||||||
|
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||||
|
(&self[..]).serialize(writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||||
|
Vec::deserialize(reader).map(|v| v.into_boxed_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/rmc/structures/connection_data.rs
Normal file
27
src/rmc/structures/connection_data.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use bytemuck::bytes_of;
|
||||||
|
use crate::kerberos::KerberosDateTime;
|
||||||
|
use crate::rmc::structures::{rmc_struct, RmcSerialize};
|
||||||
|
|
||||||
|
pub struct ConnectionData<'a>{
|
||||||
|
pub station_url: &'a str,
|
||||||
|
pub special_protocols: Vec<u8>,
|
||||||
|
pub special_station_url: &'a str,
|
||||||
|
pub date_time: KerberosDateTime
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RmcSerialize for ConnectionData<'a>{
|
||||||
|
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||||
|
rmc_struct::write_struct(writer, 1, |mut v|{
|
||||||
|
self.station_url.serialize(v).expect("unable to write station url");
|
||||||
|
self.special_protocols.serialize(v).expect("unable to write special protocols");
|
||||||
|
self.special_station_url.serialize(v).expect("unable to write special station url");
|
||||||
|
v.write_all(bytes_of(&self.date_time)).expect("unable to write date time");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
src/rmc/structures/list.rs
Normal file
21
src/rmc/structures/list.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use bytemuck::bytes_of;
|
||||||
|
use crate::rmc::structures::RmcSerialize;
|
||||||
|
|
||||||
|
|
||||||
|
impl<T: RmcSerialize> RmcSerialize for Vec<T>{
|
||||||
|
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||||
|
let u32_len = self.len();
|
||||||
|
|
||||||
|
writer.write_all(bytes_of(&u32_len))?;
|
||||||
|
for e in self{
|
||||||
|
e.serialize(writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,11 @@ type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
pub mod string;
|
pub mod string;
|
||||||
pub mod any;
|
pub mod any;
|
||||||
|
pub mod qresult;
|
||||||
|
pub mod buffer;
|
||||||
|
pub mod connection_data;
|
||||||
|
pub mod rmc_struct;
|
||||||
|
pub mod list;
|
||||||
|
|
||||||
pub trait RmcSerialize: Sized{
|
pub trait RmcSerialize: Sized{
|
||||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()>;
|
fn serialize(&self, writer: &mut dyn Write) -> Result<()>;
|
||||||
|
|
|
||||||
37
src/rmc/structures/qresult.rs
Normal file
37
src/rmc/structures/qresult.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||||
|
use v_byte_macros::SwapEndian;
|
||||||
|
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
||||||
|
use crate::rmc::response::ErrorCode;
|
||||||
|
use crate::rmc::structures::{RmcSerialize, Result};
|
||||||
|
|
||||||
|
const ERROR_MASK: u32 = 1 << 31;
|
||||||
|
|
||||||
|
#[derive(Pod, Zeroable, Copy, Clone, SwapEndian)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct QResult(u32);
|
||||||
|
|
||||||
|
impl QResult{
|
||||||
|
pub fn success(error_code: ErrorCode) -> Self{
|
||||||
|
let val: u32 = error_code.into();
|
||||||
|
|
||||||
|
Self(val & (!ERROR_MASK))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error(error_code: ErrorCode) -> Self{
|
||||||
|
let val: u32 = error_code.into();
|
||||||
|
|
||||||
|
Self(val | ERROR_MASK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RmcSerialize for QResult{
|
||||||
|
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||||
|
writer.write(bytes_of(self))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||||
|
Ok(reader.read_struct(IS_BIG_ENDIAN)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/rmc/structures/rmc_struct.rs
Normal file
24
src/rmc/structures/rmc_struct.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
use std::io::Write;
|
||||||
|
use bytemuck::bytes_of;
|
||||||
|
use crate::rmc::structures::Result;
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct StructureHeader{
|
||||||
|
version: u8,
|
||||||
|
length: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_struct(mut writer: &mut dyn Write, version: u8, pred: impl Fn(&mut Vec<u8>)) -> Result<()> {
|
||||||
|
writer.write_all(&[version])?;
|
||||||
|
|
||||||
|
let mut scratch_space: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
(pred)(&mut scratch_space);
|
||||||
|
|
||||||
|
let u32_size= scratch_space.len() as u32;
|
||||||
|
|
||||||
|
writer.write_all(bytes_of(&u32_size))?;
|
||||||
|
writer.write_all(&scratch_space)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,15 @@ impl RmcSerialize for String{
|
||||||
|
|
||||||
Ok(String::from_utf8(data)?)
|
Ok(String::from_utf8(data)?)
|
||||||
}
|
}
|
||||||
|
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||||
|
(&self[..]).serialize(writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RmcSerialize for &str{
|
||||||
|
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||||
|
panic!("cannot serialize to &str")
|
||||||
|
}
|
||||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||||
let u16_len: u16 = self.len() as u16;
|
let u16_len: u16 = self.len() as u16;
|
||||||
writer.write(bytes_of(&u16_len))?;
|
writer.write(bytes_of(&u16_len))?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue