use crate::asn1::enc_kdc_rep_part::EncKdcRepPart;
use crate::asn1::{last_req::LastReqItem, transited_encoding::TransitedEncoding, OctetString};
use crate::constants::PBKDF2_SHA1_ITER;
use crate::proto::reply::KerberosReply;
use crate::proto::{
AuthenticationTimeBound, DerivedKey, EncTicket, EncTicketPart, EncryptedData, KdcEncryptionKey,
KdcPrimaryKey, KerberosTime, KrbError, LastRequestItem, Name, PreauthData, SessionKey,
TicketFlags,
};
use std::time::SystemTime;
#[derive(Debug)]
pub struct AuthenticationReply {
pub name: Name,
pub enc_part: EncryptedData,
pub(crate) pa_data: Option<PreauthData>,
pub ticket: EncTicket,
}
pub struct AuthenticationReplyBuilder {
pub(crate) aes256_cts_hmac_sha1_96_iter_count: u32,
pub(crate) salt: Option<String>,
pub(crate) client: Name,
pub(crate) server: Name,
pub(crate) nonce: i32,
pub(crate) time_bounds: AuthenticationTimeBound,
pub(crate) flags: TicketFlags,
}
impl AuthenticationReplyBuilder {
pub fn new(
client: Name,
server: Name,
time_bounds: AuthenticationTimeBound,
nonce: i32,
) -> Self {
let aes256_cts_hmac_sha1_96_iter_count: u32 = PBKDF2_SHA1_ITER;
let mut flags = TicketFlags::none();
if time_bounds.renew_until().is_some() {
flags |= TicketFlags::Renewable;
}
Self {
aes256_cts_hmac_sha1_96_iter_count,
salt: None,
client,
server,
nonce,
time_bounds,
flags,
}
}
pub fn set_salt(mut self, salt: Option<String>) -> Self {
self.salt = salt;
self
}
pub fn set_aes256_cts_hmac_sha1_96_iter_count(mut self, iter_count: u32) -> Self {
self.aes256_cts_hmac_sha1_96_iter_count = iter_count;
self
}
pub fn build(
self,
user_key: &DerivedKey,
primary_key: &KdcPrimaryKey,
) -> Result<KerberosReply, KrbError> {
let session_key = SessionKey::new();
let session_key: KdcEncryptionKey = session_key.try_into()?;
let (cname, crealm) = (&self.client).try_into()?;
let (server_name, server_realm) = (&self.server).try_into()?;
let auth_time = KerberosTime::from_system_time(self.time_bounds.auth_time())
.map_err(|_| KrbError::DerEncodeKerberosTime)?;
let start_time = Some(
KerberosTime::from_system_time(self.time_bounds.start_time())
.map_err(|_| KrbError::DerEncodeKerberosTime)?,
);
let end_time = KerberosTime::from_system_time(self.time_bounds.end_time())
.map_err(|_| KrbError::DerEncodeKerberosTime)?;
let renew_till = self
.time_bounds
.renew_until()
.map(KerberosTime::from_system_time)
.transpose()
.map_err(|_| KrbError::DerEncodeKerberosTime)?;
let last_req: Vec<LastRequestItem> = vec![LastRequestItem::None(SystemTime::UNIX_EPOCH)];
let last_req = last_req
.iter()
.map(|i| i.try_into())
.collect::<Result<Vec<LastReqItem>, KrbError>>()?;
let enc_kdc_rep_part = EncKdcRepPart {
key: session_key.clone(),
last_req,
nonce: self.nonce,
key_expiration: None,
flags: self.flags,
auth_time,
start_time,
end_time,
renew_till,
server_realm,
server_name,
client_addresses: None,
encrypted_pa_data: None,
};
let (etype_info2, enc_part) = user_key.encrypt_as_rep_part(enc_kdc_rep_part)?;
let transited = TransitedEncoding {
tr_type: 1,
contents: OctetString::new(*b"").map_err(|_| KrbError::DerEncodeOctetString)?,
};
let ticket_inner = EncTicketPart {
flags: self.flags,
key: session_key,
crealm,
cname,
transited,
auth_time,
start_time,
end_time,
renew_till,
client_addresses: None,
authorization_data: None,
};
let ticket_enc_part = primary_key.encrypt_tgt(ticket_inner)?;
let ticket = EncTicket {
tkt_vno: 5,
service: self.server,
enc_part: ticket_enc_part,
};
let name = self.client;
let pa_data = Some(PreauthData {
etype_info2: vec![etype_info2],
..Default::default()
});
Ok(KerberosReply::AS(AuthenticationReply {
name,
enc_part,
pa_data,
ticket,
}))
}
}