rust-nex/rnex-core/src/kerberos/mod.rs

206 lines
5.3 KiB
Rust
Raw Normal View History

2026-01-31 13:48:06 +01:00
use bytemuck::{Pod, Zeroable, bytes_of};
2026-02-01 20:59:23 +01:00
use cfg_if::cfg_if;
2025-02-04 16:31:56 +01:00
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
use hmac::Hmac;
2026-01-31 13:48:06 +01:00
use hmac::Mac;
use md5::{Digest, Md5};
2026-01-31 13:48:06 +01:00
use rc4::KeyInit;
use rc4::cipher::StreamCipherCoreWrapper;
2026-01-31 13:48:06 +01:00
use rc4::{Rc4, Rc4Core, StreamCipher};
2026-02-01 20:59:23 +01:00
use rnex_core::rmc::structures::RmcSerialize;
2026-01-31 13:48:06 +01:00
use std::io::{Read, Write};
2026-03-24 15:48:56 +01:00
use typenum::U16;
2026-02-01 20:59:23 +01:00
use typenum::Unsigned;
use rnex_core::rmc::structures::Result;
2026-03-24 15:48:56 +01:00
use rnex_core::PID;
2026-02-01 20:59:23 +01:00
cfg_if! {
if #[cfg(feature = "friends")]{
pub type SESSION_KEY_LENGTH_TY = U16;
} else {
2026-03-24 15:48:56 +01:00
use rc4::consts::U32;
2026-02-01 20:59:23 +01:00
pub type SESSION_KEY_LENGTH_TY = U32;
}
}
pub const SESSION_KEY_LENGTH: usize = SESSION_KEY_LENGTH_TY::USIZE;
type Md5Hmac = Hmac<md5::Md5>;
2026-03-24 15:48:56 +01:00
pub fn derive_key(pid: PID, password: &[u8]) -> [u8; 16] {
2026-02-01 21:31:32 +01:00
let iteration_count = 65000 + pid % 1024;
2026-01-31 13:48:06 +01:00
// we do one iteration out here to ensure the key is always 16 bytes
2026-01-31 13:48:06 +01:00
let mut key: [u8; 16] = {
let mut md5 = Md5::new();
md5.update(password);
md5.finalize().try_into().unwrap()
};
2026-02-01 21:31:32 +01:00
for _ in 1..iteration_count {
let mut md5 = Md5::new();
md5.update(key);
key = md5.finalize().try_into().unwrap();
}
key
}
#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq, Default)]
#[repr(transparent)]
2025-02-06 17:54:38 +01:00
pub struct KerberosDateTime(pub u64);
2026-01-31 13:48:06 +01:00
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))
}
2026-01-31 13:48:06 +01:00
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,
)
}
2025-02-04 16:31:56 +01:00
#[inline]
2026-01-31 13:48:06 +01:00
pub fn get_seconds(&self) -> u8 {
2025-02-04 16:31:56 +01:00
(self.0 & 0b111111) as u8
}
#[inline]
2026-01-31 13:48:06 +01:00
pub fn get_minutes(&self) -> u8 {
2025-02-04 16:31:56 +01:00
((self.0 >> 6) & 0b111111) as u8
}
#[inline]
2026-01-31 13:48:06 +01:00
pub fn get_hours(&self) -> u8 {
2025-02-04 16:31:56 +01:00
((self.0 >> 12) & 0b111111) as u8
}
#[inline]
2026-01-31 13:48:06 +01:00
pub fn get_days(&self) -> u8 {
2025-02-04 16:31:56 +01:00
((self.0 >> 17) & 0b111111) as u8
}
#[inline]
2026-01-31 13:48:06 +01:00
pub fn get_month(&self) -> u8 {
2025-02-12 18:46:29 +01:00
((self.0 >> 22) & 0b1111) as u8
2025-02-04 16:31:56 +01:00
}
#[inline]
2026-01-31 13:48:06 +01:00
pub fn get_year(&self) -> u64 {
(self.0 >> 26) & 0xFFFFFFFF
2025-02-04 16:31:56 +01:00
}
2026-01-31 13:48:06 +01:00
pub fn to_regular_time(&self) -> chrono::DateTime<Utc> {
2025-02-04 16:31:56 +01:00
NaiveDateTime::new(
2026-01-31 13:48:06 +01:00
NaiveDate::from_ymd_opt(
self.get_year() as i32,
self.get_month() as u32,
self.get_days() as u32,
)
.unwrap(),
NaiveTime::from_hms_opt(
self.get_hours() as u32,
self.get_minutes() as u32,
self.get_seconds() as u32,
)
.unwrap(),
)
.and_utc()
2025-02-04 16:31:56 +01:00
}
}
2026-01-31 13:48:06 +01:00
impl RmcSerialize for KerberosDateTime {
2026-02-01 20:59:23 +01:00
fn serialize(&self, writer: &mut impl Write) -> Result<()> {
Ok(self.0.serialize(writer)?)
}
2026-02-01 20:59:23 +01:00
fn deserialize(reader: &mut impl Read) -> Result<Self> {
Ok(Self(u64::deserialize(reader)?))
}
}
#[derive(Pod, Zeroable, Copy, Clone)]
#[repr(C, packed)]
2026-01-31 13:48:06 +01:00
pub struct TicketInternalData {
2025-02-04 16:31:56 +01:00
pub issued_time: KerberosDateTime,
2026-03-24 15:48:56 +01:00
pub pid: PID,
2026-02-01 20:59:23 +01:00
pub session_key: [u8; SESSION_KEY_LENGTH],
}
2026-01-31 13:48:06 +01:00
impl TicketInternalData {
2026-03-24 15:48:56 +01:00
pub(crate) fn new(pid: PID) -> Self {
2026-01-31 13:48:06 +01:00
Self {
issued_time: KerberosDateTime::now(),
pid,
2026-01-31 13:48:06 +01:00
session_key: rand::random(),
}
}
2026-01-31 13:48:06 +01:00
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();
2026-01-31 13:48:06 +01:00
hmac.write_all(&data[..])
.expect("failed to write data to hmac");
let hmac_result = &hmac.finalize().into_bytes()[..];
2026-01-31 13:48:06 +01:00
data.write_all(&hmac_result)
.expect("failed to write data to vec");
data.into_boxed_slice()
}
}
#[derive(Pod, Zeroable, Copy, Clone)]
#[repr(C, packed)]
2026-01-31 13:48:06 +01:00
pub struct Ticket {
2026-02-01 20:59:23 +01:00
pub session_key: [u8; SESSION_KEY_LENGTH],
2026-03-24 15:48:56 +01:00
pub pid: PID,
}
2026-01-31 13:48:06 +01:00
impl Ticket {
pub fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]> {
let mut data = bytes_of(self).to_vec();
2026-01-31 13:48:06 +01:00
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();
2026-01-31 13:48:06 +01:00
hmac.write_all(&data[..])
.expect("failed to write data to hmac");
let hmac_result = &hmac.finalize().into_bytes()[..];
2026-01-31 13:48:06 +01:00
data.write_all(&hmac_result)
.expect("failed to write data to vec");
data.into_boxed_slice()
}
}
2025-02-12 18:46:29 +01:00
#[cfg(test)]
2026-01-31 13:48:06 +01:00
mod test {
2025-02-12 18:46:29 +01:00
use crate::kerberos::KerberosDateTime;
#[test]
2026-01-31 13:48:06 +01:00
fn kerberos_time_convert_test() {
2025-02-12 18:46:29 +01:00
let time = KerberosDateTime(135904948834);
println!("{}", time.to_regular_time().to_rfc2822());
}
2026-01-31 13:48:06 +01:00
}