use bytemuck::{Pod, Zeroable, bytes_of}; use cfg_if::cfg_if; use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; use hmac::Hmac; use hmac::Mac; use md5::{Digest, Md5}; use rc4::KeyInit; use rc4::cipher::StreamCipherCoreWrapper; use rc4::{Rc4, Rc4Core, StreamCipher}; use rnex_core::rmc::structures::RmcSerialize; use std::io::{Read, Write}; use typenum::U16; use typenum::Unsigned; use rnex_core::rmc::structures::Result; use rnex_core::PID; cfg_if! { if #[cfg(feature = "friends")]{ pub type SESSION_KEY_LENGTH_TY = U16; } else { use rc4::consts::U32; pub type SESSION_KEY_LENGTH_TY = U32; } } pub const SESSION_KEY_LENGTH: usize = SESSION_KEY_LENGTH_TY::USIZE; type Md5Hmac = Hmac; pub fn derive_key(pid: PID, password: &[u8]) -> [u8; 16] { let iteration_count = 65000 + pid % 1024; // we do one iteration out here to ensure the key is always 16 bytes let mut key: [u8; 16] = { let mut md5 = Md5::new(); md5.update(password); md5.finalize().try_into().unwrap() }; 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)] pub struct KerberosDateTime(pub 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, ) } #[inline] pub fn get_seconds(&self) -> u8 { (self.0 & 0b111111) as u8 } #[inline] pub fn get_minutes(&self) -> u8 { ((self.0 >> 6) & 0b111111) as u8 } #[inline] pub fn get_hours(&self) -> u8 { ((self.0 >> 12) & 0b111111) as u8 } #[inline] pub fn get_days(&self) -> u8 { ((self.0 >> 17) & 0b111111) as u8 } #[inline] pub fn get_month(&self) -> u8 { ((self.0 >> 22) & 0b1111) as u8 } #[inline] pub fn get_year(&self) -> u64 { (self.0 >> 26) & 0xFFFFFFFF } pub fn to_regular_time(&self) -> chrono::DateTime { NaiveDateTime::new( 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() } } impl RmcSerialize for KerberosDateTime { fn serialize(&self, writer: &mut impl Write) -> Result<()> { Ok(self.0.serialize(writer)?) } fn deserialize(reader: &mut impl Read) -> Result { Ok(Self(u64::deserialize(reader)?)) } } #[derive(Pod, Zeroable, Copy, Clone)] #[repr(C, packed)] pub struct TicketInternalData { pub issued_time: KerberosDateTime, pub pid: PID, pub session_key: [u8; SESSION_KEY_LENGTH], } impl TicketInternalData { pub(crate) fn new(pid: PID) -> 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> = Rc4::new_from_slice(&key).unwrap(); rc4.apply_keystream(&mut data); let mut hmac = ::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, Debug, Copy, Clone)] #[repr(C, packed)] pub struct Ticket { pub session_key: [u8; SESSION_KEY_LENGTH], pub pid: PID, } impl Ticket { pub 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> = Rc4::new_from_slice(&key).unwrap(); rc4.apply_keystream(&mut data); let mut hmac = ::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() } } #[cfg(test)] mod test { use crate::kerberos::KerberosDateTime; #[test] fn kerberos_time_convert_test() { let time = KerberosDateTime(135904948834); println!("{}", time.to_regular_time().to_rfc2822()); } }