use crate::auth::der::{der_tlv, parse_der_tlv};
use crate::auth::kerberos::crypto::EncryptionType;
use crate::Error;
const TAG_INTEGER: u8 = 0x02;
const TAG_BIT_STRING: u8 = 0x03;
const TAG_OCTET_STRING: u8 = 0x04;
const TAG_GENERAL_STRING: u8 = 0x1b;
const TAG_GENERALIZED_TIME: u8 = 0x18;
const TAG_SEQUENCE: u8 = 0x30;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PrincipalName {
pub name_type: i32,
pub name_string: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ticket {
pub tkt_vno: i32,
pub realm: String,
pub sname: PrincipalName,
pub enc_part: EncryptedData,
pub raw_bytes: Option<Vec<u8>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncryptedData {
pub etype: i32,
pub kvno: Option<i32>,
pub cipher: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PaData {
pub padata_type: i32,
pub padata_value: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KdcRep {
pub msg_type: i32,
pub crealm: String,
pub cname: PrincipalName,
pub ticket: Ticket,
pub enc_part: EncryptedData,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncKdcRepPart {
pub key: EncryptionKey,
pub nonce: u32,
pub flags: u32,
pub authtime: String,
pub endtime: String,
pub srealm: String,
pub sname: PrincipalName,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncryptionKey {
pub keytype: i32,
pub keyvalue: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ApRep {
pub enc_part: EncryptedData,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncApRepPart {
pub subkey: Option<EncryptionKey>,
pub seq_number: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KrbError {
pub error_code: i32,
pub crealm: Option<String>,
pub realm: String,
pub sname: PrincipalName,
pub e_text: Option<String>,
pub e_data: Option<Vec<u8>>,
}
fn der_context(tag_num: u8, data: &[u8]) -> Vec<u8> {
der_tlv(0xa0 | tag_num, data)
}
fn der_application(tag_num: u8, data: &[u8]) -> Vec<u8> {
der_tlv(0x60 | tag_num, data)
}
fn der_integer(val: i32) -> Vec<u8> {
let bytes = val.to_be_bytes();
let mut start = 0;
if val >= 0 {
while start < 3 && bytes[start] == 0x00 && bytes[start + 1] & 0x80 == 0 {
start += 1;
}
} else {
while start < 3 && bytes[start] == 0xff && bytes[start + 1] & 0x80 != 0 {
start += 1;
}
}
der_tlv(TAG_INTEGER, &bytes[start..])
}
fn der_integer_u32(val: u32) -> Vec<u8> {
let val64 = val as i64;
let bytes = val64.to_be_bytes();
let mut start = 0;
while start < 7 && bytes[start] == 0x00 && bytes[start + 1] & 0x80 == 0 {
start += 1;
}
der_tlv(TAG_INTEGER, &bytes[start..])
}
fn der_octet_string(data: &[u8]) -> Vec<u8> {
der_tlv(TAG_OCTET_STRING, data)
}
fn der_general_string(s: &str) -> Vec<u8> {
der_tlv(TAG_GENERAL_STRING, s.as_bytes())
}
fn der_generalized_time(time: &str) -> Vec<u8> {
der_tlv(TAG_GENERALIZED_TIME, time.as_bytes())
}
fn der_bit_string(bits: &[u8], unused: u8) -> Vec<u8> {
let mut data = vec![unused];
data.extend_from_slice(bits);
der_tlv(TAG_BIT_STRING, &data)
}
fn der_sequence(items: &[&[u8]]) -> Vec<u8> {
let mut contents = Vec::new();
for item in items {
contents.extend_from_slice(item);
}
der_tlv(TAG_SEQUENCE, &contents)
}
fn parse_sequence_fields(data: &[u8]) -> Result<Vec<(u8, Vec<u8>)>, Error> {
let mut fields = Vec::new();
let mut pos = 0;
while pos < data.len() {
let (tag, value, consumed) = parse_der_tlv(&data[pos..])?;
fields.push((tag, value.to_vec()));
pos += consumed;
}
Ok(fields)
}
fn parse_integer_value(data: &[u8]) -> Result<i32, Error> {
if data.is_empty() {
return Err(Error::invalid_data("Kerberos: empty INTEGER"));
}
let negative = data[0] & 0x80 != 0;
let mut val: i64 = if negative { -1 } else { 0 };
for &b in data {
val = (val << 8) | (b as i64);
}
Ok(val as i32)
}
fn parse_der_integer(data: &[u8]) -> Result<i32, Error> {
let (tag, value, _) = parse_der_tlv(data)?;
if tag != TAG_INTEGER {
return Err(Error::invalid_data(format!(
"Kerberos: expected INTEGER (0x02), got 0x{tag:02x}"
)));
}
parse_integer_value(value)
}
fn parse_der_integer_u32(data: &[u8]) -> Result<u32, Error> {
let val = parse_der_integer(data)?;
Ok(val as u32)
}
fn parse_der_octet_string(data: &[u8]) -> Result<Vec<u8>, Error> {
let (tag, value, _) = parse_der_tlv(data)?;
if tag != TAG_OCTET_STRING {
return Err(Error::invalid_data(format!(
"Kerberos: expected OCTET STRING (0x04), got 0x{tag:02x}"
)));
}
Ok(value.to_vec())
}
fn parse_der_general_string(data: &[u8]) -> Result<String, Error> {
let (tag, value, _) = parse_der_tlv(data)?;
if tag != TAG_GENERAL_STRING {
return Err(Error::invalid_data(format!(
"Kerberos: expected GeneralString (0x1b), got 0x{tag:02x}"
)));
}
String::from_utf8(value.to_vec())
.map_err(|_| Error::invalid_data("Kerberos: invalid UTF-8 in GeneralString"))
}
fn parse_der_generalized_time(data: &[u8]) -> Result<String, Error> {
let (tag, value, _) = parse_der_tlv(data)?;
if tag != TAG_GENERALIZED_TIME {
return Err(Error::invalid_data(format!(
"Kerberos: expected GeneralizedTime (0x18), got 0x{tag:02x}"
)));
}
String::from_utf8(value.to_vec())
.map_err(|_| Error::invalid_data("Kerberos: invalid UTF-8 in GeneralizedTime"))
}
fn parse_der_bit_string(data: &[u8]) -> Result<(Vec<u8>, u8), Error> {
let (tag, value, _) = parse_der_tlv(data)?;
if tag != TAG_BIT_STRING {
return Err(Error::invalid_data(format!(
"Kerberos: expected BIT STRING (0x03), got 0x{tag:02x}"
)));
}
if value.is_empty() {
return Err(Error::invalid_data("Kerberos: empty BIT STRING"));
}
let unused = value[0];
Ok((value[1..].to_vec(), unused))
}
fn encode_principal_name(name: &PrincipalName) -> Vec<u8> {
let name_type = der_context(0, &der_integer(name.name_type));
let name_strings: Vec<Vec<u8>> = name
.name_string
.iter()
.map(|s| der_general_string(s))
.collect();
let name_refs: Vec<&[u8]> = name_strings.iter().map(|v| v.as_slice()).collect();
let name_seq = der_sequence(&name_refs);
let name_string = der_context(1, &name_seq);
der_sequence(&[&name_type, &name_string])
}
fn encode_encrypted_data(ed: &EncryptedData) -> Vec<u8> {
let etype = der_context(0, &der_integer(ed.etype));
let cipher = der_context(2, &der_octet_string(&ed.cipher));
if let Some(kvno) = ed.kvno {
let kvno_enc = der_context(1, &der_integer(kvno));
der_sequence(&[&etype, &kvno_enc, &cipher])
} else {
der_sequence(&[&etype, &cipher])
}
}
fn encode_ticket(ticket: &Ticket) -> Vec<u8> {
let tkt_vno = der_context(0, &der_integer(ticket.tkt_vno));
let realm = der_context(1, &der_general_string(&ticket.realm));
let sname = der_context(2, &encode_principal_name(&ticket.sname));
let enc_part = der_context(3, &encode_encrypted_data(&ticket.enc_part));
let seq = der_sequence(&[&tkt_vno, &realm, &sname, &enc_part]);
der_application(1, &seq)
}
fn encode_pa_data(pa: &PaData) -> Vec<u8> {
let padata_type = der_context(1, &der_integer(pa.padata_type));
let padata_value = der_context(2, &der_octet_string(&pa.padata_value));
der_sequence(&[&padata_type, &padata_value])
}
fn parse_principal_name(data: &[u8]) -> Result<PrincipalName, Error> {
let (tag, seq_data, _) = parse_der_tlv(data)?;
if tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE for PrincipalName, got 0x{tag:02x}"
)));
}
let fields = parse_sequence_fields(seq_data)?;
let mut name_type = None;
let mut name_string = Vec::new();
for (ftag, fvalue) in &fields {
match ftag {
0xa0 => name_type = Some(parse_der_integer(fvalue)?),
0xa1 => {
let (stag, sdata, _) = parse_der_tlv(fvalue)?;
if stag != TAG_SEQUENCE {
return Err(Error::invalid_data(
"Kerberos: expected SEQUENCE for name-string",
));
}
let mut pos = 0;
while pos < sdata.len() {
let (_, sv, consumed) = parse_der_tlv(&sdata[pos..])?;
name_string.push(String::from_utf8(sv.to_vec()).map_err(|_| {
Error::invalid_data("Kerberos: invalid UTF-8 in name-string")
})?);
pos += consumed;
}
}
_ => {} }
}
Ok(PrincipalName {
name_type: name_type
.ok_or_else(|| Error::invalid_data("Kerberos: missing name-type in PrincipalName"))?,
name_string,
})
}
fn parse_encrypted_data(data: &[u8]) -> Result<EncryptedData, Error> {
let (tag, seq_data, _) = parse_der_tlv(data)?;
if tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE for EncryptedData, got 0x{tag:02x}"
)));
}
let fields = parse_sequence_fields(seq_data)?;
let mut etype = None;
let mut kvno = None;
let mut cipher = None;
for (ftag, fvalue) in &fields {
match ftag {
0xa0 => etype = Some(parse_der_integer(fvalue)?),
0xa1 => kvno = Some(parse_der_integer(fvalue)?),
0xa2 => cipher = Some(parse_der_octet_string(fvalue)?),
_ => {}
}
}
Ok(EncryptedData {
etype: etype
.ok_or_else(|| Error::invalid_data("Kerberos: missing etype in EncryptedData"))?,
kvno,
cipher: cipher
.ok_or_else(|| Error::invalid_data("Kerberos: missing cipher in EncryptedData"))?,
})
}
pub fn parse_ticket(data: &[u8]) -> Result<Ticket, Error> {
let (tag, inner, total_consumed) = parse_der_tlv(data)?;
if tag != 0x61 {
return Err(Error::invalid_data(format!(
"Kerberos: expected APPLICATION [1] (0x61) for Ticket, got 0x{tag:02x}"
)));
}
let raw_bytes = data[..total_consumed].to_vec();
let (seq_tag, seq_data, _) = parse_der_tlv(inner)?;
if seq_tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE in Ticket, got 0x{seq_tag:02x}"
)));
}
let fields = parse_sequence_fields(seq_data)?;
let mut tkt_vno = None;
let mut realm = None;
let mut sname = None;
let mut enc_part = None;
for (ftag, fvalue) in &fields {
match ftag {
0xa0 => tkt_vno = Some(parse_der_integer(fvalue)?),
0xa1 => realm = Some(parse_der_general_string(fvalue)?),
0xa2 => sname = Some(parse_principal_name(fvalue)?),
0xa3 => enc_part = Some(parse_encrypted_data(fvalue)?),
_ => {}
}
}
Ok(Ticket {
tkt_vno: tkt_vno
.ok_or_else(|| Error::invalid_data("Kerberos: missing tkt-vno in Ticket"))?,
realm: realm.ok_or_else(|| Error::invalid_data("Kerberos: missing realm in Ticket"))?,
sname: sname.ok_or_else(|| Error::invalid_data("Kerberos: missing sname in Ticket"))?,
enc_part: enc_part
.ok_or_else(|| Error::invalid_data("Kerberos: missing enc-part in Ticket"))?,
raw_bytes: Some(raw_bytes),
})
}
fn parse_encryption_key(data: &[u8]) -> Result<EncryptionKey, Error> {
let (tag, seq_data, _) = parse_der_tlv(data)?;
if tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE for EncryptionKey, got 0x{tag:02x}"
)));
}
let fields = parse_sequence_fields(seq_data)?;
let mut keytype = None;
let mut keyvalue = None;
for (ftag, fvalue) in &fields {
match ftag {
0xa0 => keytype = Some(parse_der_integer(fvalue)?),
0xa1 => keyvalue = Some(parse_der_octet_string(fvalue)?),
_ => {}
}
}
Ok(EncryptionKey {
keytype: keytype
.ok_or_else(|| Error::invalid_data("Kerberos: missing keytype in EncryptionKey"))?,
keyvalue: keyvalue
.ok_or_else(|| Error::invalid_data("Kerberos: missing keyvalue in EncryptionKey"))?,
})
}
pub fn encode_as_req(
cname: &PrincipalName,
realm: &str,
sname: &PrincipalName,
nonce: u32,
etypes: &[EncryptionType],
padata: &[PaData],
) -> Vec<u8> {
encode_kdc_req(10, Some(cname), realm, sname, nonce, etypes, padata)
}
pub fn encode_tgs_req_body(
realm: &str,
sname: &PrincipalName,
nonce: u32,
etypes: &[EncryptionType],
) -> Vec<u8> {
encode_kdc_req_body(None, realm, sname, nonce, etypes)
}
pub fn encode_tgs_req(
realm: &str,
sname: &PrincipalName,
nonce: u32,
etypes: &[EncryptionType],
tgt_ap_req: &[u8],
) -> Vec<u8> {
let padata = [PaData {
padata_type: 1, padata_value: tgt_ap_req.to_vec(),
}];
encode_kdc_req(12, None, realm, sname, nonce, etypes, &padata)
}
pub fn encode_ap_req(
ticket: &Ticket,
encrypted_authenticator: &EncryptedData,
mutual_required: bool,
) -> Vec<u8> {
let pvno = der_context(0, &der_integer(5));
let msg_type = der_context(1, &der_integer(14));
let opts_byte0 = if mutual_required { 0x20 } else { 0x00 };
let ap_options = der_context(2, &der_bit_string(&[opts_byte0, 0x00, 0x00, 0x00], 0));
let ticket_raw = ticket
.raw_bytes
.as_ref()
.map(|b| der_context(3, b))
.unwrap_or_else(|| der_context(3, &encode_ticket(ticket)));
let authenticator = der_context(4, &encode_encrypted_data(encrypted_authenticator));
let seq = der_sequence(&[&pvno, &msg_type, &ap_options, &ticket_raw, &authenticator]);
der_application(14, &seq)
}
pub fn encode_authenticator(
crealm: &str,
cname: &PrincipalName,
ctime: &str,
cusec: u32,
subkey: Option<(&[u8], i32)>,
seq_number: Option<u32>,
cksum: Option<(&[u8], i32)>,
) -> Vec<u8> {
let auth_vno = der_context(0, &der_integer(5));
let crealm_enc = der_context(1, &der_general_string(crealm));
let cname_enc = der_context(2, &encode_principal_name(cname));
let mut items: Vec<Vec<u8>> = vec![auth_vno, crealm_enc, cname_enc];
if let Some((checksum_data, checksum_type)) = cksum {
let cktype = der_context(0, &der_integer(checksum_type));
let ckval = der_context(1, &der_octet_string(checksum_data));
let ck = der_sequence(&[&cktype, &ckval]);
items.push(der_context(3, &ck));
}
let cusec_enc = der_context(4, &der_integer_u32(cusec));
let ctime_enc = der_context(5, &der_generalized_time(ctime));
items.push(cusec_enc);
items.push(ctime_enc);
if let Some((key_value, key_type)) = subkey {
let kt = der_context(0, &der_integer(key_type));
let kv = der_context(1, &der_octet_string(key_value));
let ek = der_sequence(&[&kt, &kv]);
items.push(der_context(6, &ek));
}
if let Some(seq) = seq_number {
items.push(der_context(7, &der_integer_u32(seq)));
}
let item_refs: Vec<&[u8]> = items.iter().map(|v| v.as_slice()).collect();
let seq = der_sequence(&item_refs);
der_application(2, &seq)
}
pub fn encode_pa_enc_timestamp(ctime: &str, cusec: u32) -> Vec<u8> {
let patimestamp = der_context(0, &der_generalized_time(ctime));
let pausec = der_context(1, &der_integer_u32(cusec));
der_sequence(&[&patimestamp, &pausec])
}
pub fn parse_kdc_rep(data: &[u8]) -> Result<KdcRep, Error> {
let (tag, inner, _) = parse_der_tlv(data)?;
let expected_msg_type = match tag {
0x6b => 11,
0x6d => 13,
_ => {
return Err(Error::invalid_data(format!(
"Kerberos: expected APPLICATION [11] or [13] for KDC-REP, got 0x{tag:02x}"
)));
}
};
let (seq_tag, seq_data, _) = parse_der_tlv(inner)?;
if seq_tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE in KDC-REP, got 0x{seq_tag:02x}"
)));
}
let fields = parse_sequence_fields(seq_data)?;
let mut msg_type = None;
let mut crealm = None;
let mut cname = None;
let mut ticket = None;
let mut enc_part = None;
for (ftag, fvalue) in &fields {
match ftag {
0xa0 => {
}
0xa1 => msg_type = Some(parse_der_integer(fvalue)?),
0xa3 => crealm = Some(parse_der_general_string(fvalue)?),
0xa4 => cname = Some(parse_principal_name(fvalue)?),
0xa5 => ticket = Some(parse_ticket(fvalue)?),
0xa6 => enc_part = Some(parse_encrypted_data(fvalue)?),
_ => {}
}
}
let msg_type =
msg_type.ok_or_else(|| Error::invalid_data("Kerberos: missing msg-type in KDC-REP"))?;
if msg_type != expected_msg_type {
return Err(Error::invalid_data(format!(
"Kerberos: KDC-REP msg-type mismatch: tag says {expected_msg_type}, field says {msg_type}"
)));
}
Ok(KdcRep {
msg_type,
crealm: crealm.ok_or_else(|| Error::invalid_data("Kerberos: missing crealm in KDC-REP"))?,
cname: cname.ok_or_else(|| Error::invalid_data("Kerberos: missing cname in KDC-REP"))?,
ticket: ticket.ok_or_else(|| Error::invalid_data("Kerberos: missing ticket in KDC-REP"))?,
enc_part: enc_part
.ok_or_else(|| Error::invalid_data("Kerberos: missing enc-part in KDC-REP"))?,
})
}
pub fn parse_enc_kdc_rep_part(data: &[u8]) -> Result<EncKdcRepPart, Error> {
let (tag, inner, _) = parse_der_tlv(data)?;
let seq_data = if tag == 0x79 || tag == 0x7a {
let (seq_tag, sd, _) = parse_der_tlv(inner)?;
if seq_tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE in EncKDCRepPart, got 0x{seq_tag:02x}"
)));
}
sd
} else if tag == TAG_SEQUENCE {
inner
} else {
return Err(Error::invalid_data(format!(
"Kerberos: expected APPLICATION [25/26] or SEQUENCE for EncKDCRepPart, got 0x{tag:02x}"
)));
};
let fields = parse_sequence_fields(seq_data)?;
let mut key = None;
let mut nonce = None;
let mut flags = None;
let mut authtime = None;
let mut endtime = None;
let mut srealm = None;
let mut sname = None;
for (ftag, fvalue) in &fields {
match ftag {
0xa0 => key = Some(parse_encryption_key(fvalue)?),
0xa2 => nonce = Some(parse_der_integer_u32(fvalue)?),
0xa4 => {
let (bits, _unused) = parse_der_bit_string(fvalue)?;
if bits.len() >= 4 {
flags = Some(u32::from_be_bytes([bits[0], bits[1], bits[2], bits[3]]));
}
}
0xa5 => authtime = Some(parse_der_generalized_time(fvalue)?),
0xa7 => endtime = Some(parse_der_generalized_time(fvalue)?),
0xa9 => srealm = Some(parse_der_general_string(fvalue)?),
0xaa => sname = Some(parse_principal_name(fvalue)?),
_ => {}
}
}
Ok(EncKdcRepPart {
key: key.ok_or_else(|| Error::invalid_data("Kerberos: missing key in EncKDCRepPart"))?,
nonce: nonce
.ok_or_else(|| Error::invalid_data("Kerberos: missing nonce in EncKDCRepPart"))?,
flags: flags.unwrap_or(0),
authtime: authtime
.ok_or_else(|| Error::invalid_data("Kerberos: missing authtime in EncKDCRepPart"))?,
endtime: endtime
.ok_or_else(|| Error::invalid_data("Kerberos: missing endtime in EncKDCRepPart"))?,
srealm: srealm
.ok_or_else(|| Error::invalid_data("Kerberos: missing srealm in EncKDCRepPart"))?,
sname: sname
.ok_or_else(|| Error::invalid_data("Kerberos: missing sname in EncKDCRepPart"))?,
})
}
pub fn parse_krb_error(data: &[u8]) -> Result<KrbError, Error> {
let (tag, inner, _) = parse_der_tlv(data)?;
if tag != 0x7e {
return Err(Error::invalid_data(format!(
"Kerberos: expected APPLICATION [30] (0x7e) for KRB-ERROR, got 0x{tag:02x}"
)));
}
let (seq_tag, seq_data, _) = parse_der_tlv(inner)?;
if seq_tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE in KRB-ERROR, got 0x{seq_tag:02x}"
)));
}
let fields = parse_sequence_fields(seq_data)?;
let mut error_code = None;
let mut crealm = None;
let mut realm = None;
let mut sname = None;
let mut e_text = None;
let mut e_data = None;
for (ftag, fvalue) in &fields {
match ftag {
0xa6 => error_code = Some(parse_der_integer(fvalue)?),
0xa7 => crealm = Some(parse_der_general_string(fvalue)?),
0xa8 => {
}
0xa9 => realm = Some(parse_der_general_string(fvalue)?),
0xaa => sname = Some(parse_principal_name(fvalue)?),
0xab => e_text = Some(parse_der_general_string(fvalue)?),
0xac => e_data = Some(parse_der_octet_string(fvalue)?),
_ => {}
}
}
Ok(KrbError {
error_code: error_code
.ok_or_else(|| Error::invalid_data("Kerberos: missing error-code in KRB-ERROR"))?,
crealm,
realm: realm.ok_or_else(|| Error::invalid_data("Kerberos: missing realm in KRB-ERROR"))?,
sname: sname.ok_or_else(|| Error::invalid_data("Kerberos: missing sname in KRB-ERROR"))?,
e_text,
e_data,
})
}
pub fn parse_gss_api_wrapper(data: &[u8]) -> Result<(Vec<u8>, Vec<u8>, usize), Error> {
let (tag, inner, total) = parse_der_tlv(data)?;
if tag != 0x60 {
return Err(Error::invalid_data(format!(
"Kerberos: expected GSS-API wrapper (0x60), got 0x{tag:02x}"
)));
}
let (_oid_tag, oid_data, oid_consumed) = parse_der_tlv(inner)?;
let oid = oid_data.to_vec();
let rest = inner[oid_consumed..].to_vec();
Ok((oid, rest, total))
}
pub fn parse_ap_rep(data: &[u8]) -> Result<ApRep, Error> {
let (tag, inner, _) = parse_der_tlv(data)?;
let inner = if tag == 0x60 {
let (_oid_tag, _oid_data, oid_consumed) = parse_der_tlv(inner)?;
let ap_rep_data = &inner[oid_consumed..];
let (ap_tag, ap_inner, _) = parse_der_tlv(ap_rep_data)?;
if ap_tag != 0x6f {
return Err(Error::invalid_data(format!(
"Kerberos: expected AP-REP (0x6f) inside GSS wrapper, got 0x{ap_tag:02x}"
)));
}
ap_inner
} else if tag == 0x6f {
inner
} else {
return Err(Error::invalid_data(format!(
"Kerberos: expected APPLICATION [15] (0x6f) or GSS wrapper (0x60) for AP-REP, got 0x{tag:02x}"
)));
};
let (seq_tag, seq_data, _) = parse_der_tlv(inner)?;
if seq_tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE in AP-REP, got 0x{seq_tag:02x}"
)));
}
let fields = parse_sequence_fields(seq_data)?;
let mut enc_part = None;
for (ftag, fvalue) in &fields {
if ftag == &0xa2 {
enc_part = Some(parse_encrypted_data(fvalue)?);
}
}
Ok(ApRep {
enc_part: enc_part
.ok_or_else(|| Error::invalid_data("Kerberos: missing enc-part in AP-REP"))?,
})
}
pub fn parse_enc_ap_rep_part(data: &[u8]) -> Result<EncApRepPart, Error> {
let (tag, inner, _) = parse_der_tlv(data)?;
let seq_data = match tag {
0x7b => {
let (seq_tag, seq_data, _) = parse_der_tlv(inner)?;
if seq_tag != TAG_SEQUENCE {
return Err(Error::invalid_data(format!(
"Kerberos: expected SEQUENCE in EncAPRepPart, got 0x{seq_tag:02x}"
)));
}
seq_data
}
TAG_SEQUENCE => inner,
_ => {
return Err(Error::invalid_data(format!(
"Kerberos: expected APPLICATION [27] or SEQUENCE for EncAPRepPart, got 0x{tag:02x}"
)));
}
};
let fields = parse_sequence_fields(seq_data)?;
let mut subkey = None;
let mut seq_number = None;
for (ftag, fvalue) in &fields {
match ftag {
0xa2 => subkey = Some(parse_encryption_key(fvalue)?),
0xa3 => seq_number = Some(parse_der_integer_u32(fvalue)?),
_ => {}
}
}
Ok(EncApRepPart { subkey, seq_number })
}
fn encode_kdc_req_body(
cname: Option<&PrincipalName>,
realm: &str,
sname: &PrincipalName,
nonce: u32,
etypes: &[EncryptionType],
) -> Vec<u8> {
let kdc_options = der_context(0, &der_bit_string(&[0x40, 0x81, 0x00, 0x10], 0));
let mut body_items: Vec<Vec<u8>> = vec![kdc_options];
if let Some(cn) = cname {
body_items.push(der_context(1, &encode_principal_name(cn)));
}
body_items.push(der_context(2, &der_general_string(realm)));
body_items.push(der_context(3, &encode_principal_name(sname)));
body_items.push(der_context(5, &der_generalized_time("20370913024805Z")));
body_items.push(der_context(7, &der_integer_u32(nonce)));
let etype_ints: Vec<Vec<u8>> = etypes.iter().map(|e| der_integer(*e as i32)).collect();
let etype_refs: Vec<&[u8]> = etype_ints.iter().map(|v| v.as_slice()).collect();
let etype_seq = der_sequence(&etype_refs);
body_items.push(der_context(8, &etype_seq));
let body_refs: Vec<&[u8]> = body_items.iter().map(|v| v.as_slice()).collect();
der_sequence(&body_refs)
}
fn encode_kdc_req(
msg_type_val: i32,
cname: Option<&PrincipalName>,
realm: &str,
sname: &PrincipalName,
nonce: u32,
etypes: &[EncryptionType],
padata: &[PaData],
) -> Vec<u8> {
let req_body = encode_kdc_req_body(cname, realm, sname, nonce, etypes);
let pvno = der_context(1, &der_integer(5));
let msg_type = der_context(2, &der_integer(msg_type_val));
let mut kdc_req_items: Vec<Vec<u8>> = vec![pvno, msg_type];
if !padata.is_empty() {
let pa_items: Vec<Vec<u8>> = padata.iter().map(encode_pa_data).collect();
let pa_refs: Vec<&[u8]> = pa_items.iter().map(|v| v.as_slice()).collect();
let pa_seq = der_sequence(&pa_refs);
kdc_req_items.push(der_context(3, &pa_seq));
}
kdc_req_items.push(der_context(4, &req_body));
let kdc_req_refs: Vec<&[u8]> = kdc_req_items.iter().map(|v| v.as_slice()).collect();
let kdc_req_seq = der_sequence(&kdc_req_refs);
let app_tag = match msg_type_val {
10 => 10, 12 => 12, _ => msg_type_val as u8,
};
der_application(app_tag, &kdc_req_seq)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_der_integer_positive() {
let encoded = der_integer(5);
assert_eq!(encoded, vec![0x02, 0x01, 0x05]);
}
#[test]
fn test_der_integer_zero() {
let encoded = der_integer(0);
assert_eq!(encoded, vec![0x02, 0x01, 0x00]);
}
#[test]
fn test_der_integer_negative() {
let encoded = der_integer(-1);
assert_eq!(encoded, vec![0x02, 0x01, 0xff]);
}
#[test]
fn test_der_integer_128() {
let encoded = der_integer(128);
assert_eq!(encoded, vec![0x02, 0x02, 0x00, 0x80]);
}
#[test]
fn test_der_integer_256() {
let encoded = der_integer(256);
assert_eq!(encoded, vec![0x02, 0x02, 0x01, 0x00]);
}
#[test]
fn test_der_integer_large_positive() {
let encoded = der_integer(65536);
assert_eq!(encoded, vec![0x02, 0x03, 0x01, 0x00, 0x00]);
}
#[test]
fn test_der_integer_u32_max() {
let encoded = der_integer_u32(u32::MAX);
assert_eq!(encoded, vec![0x02, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff]);
}
#[test]
fn test_der_generalized_time() {
let encoded = der_generalized_time("20260408120000Z");
assert_eq!(encoded[0], TAG_GENERALIZED_TIME);
assert_eq!(encoded[1], 15); assert_eq!(&encoded[2..], b"20260408120000Z");
}
#[test]
fn test_der_bit_string_32bit_flags() {
let encoded = der_bit_string(&[0x40, 0x81, 0x00, 0x10], 0);
assert_eq!(encoded[0], TAG_BIT_STRING);
assert_eq!(encoded[1], 5); assert_eq!(encoded[2], 0); assert_eq!(&encoded[3..], &[0x40, 0x81, 0x00, 0x10]);
}
#[test]
fn test_der_general_string() {
let encoded = der_general_string("EXAMPLE.COM");
assert_eq!(encoded[0], TAG_GENERAL_STRING);
assert_eq!(encoded[1], 11);
assert_eq!(&encoded[2..], b"EXAMPLE.COM");
}
#[test]
fn test_parse_der_integer_roundtrip() {
for val in [0, 1, 5, 127, 128, 255, 256, 1000, -1, -128, -129] {
let encoded = der_integer(val);
let parsed = parse_der_integer(&encoded).unwrap();
assert_eq!(parsed, val, "roundtrip failed for {val}");
}
}
#[test]
fn test_parse_der_octet_string_roundtrip() {
let data = vec![0x01, 0x02, 0x03, 0xff];
let encoded = der_octet_string(&data);
let parsed = parse_der_octet_string(&encoded).unwrap();
assert_eq!(parsed, data);
}
#[test]
fn test_parse_der_general_string_roundtrip() {
let encoded = der_general_string("EXAMPLE.COM");
let parsed = parse_der_general_string(&encoded).unwrap();
assert_eq!(parsed, "EXAMPLE.COM");
}
#[test]
fn test_parse_der_generalized_time_roundtrip() {
let encoded = der_generalized_time("20260408120000Z");
let parsed = parse_der_generalized_time(&encoded).unwrap();
assert_eq!(parsed, "20260408120000Z");
}
#[test]
fn test_parse_der_bit_string_roundtrip() {
let bits = vec![0x40, 0x81, 0x00, 0x10];
let encoded = der_bit_string(&bits, 0);
let (parsed_bits, unused) = parse_der_bit_string(&encoded).unwrap();
assert_eq!(parsed_bits, bits);
assert_eq!(unused, 0);
}
#[test]
fn test_encode_as_req_application_tag() {
let cname = PrincipalName {
name_type: 1,
name_string: vec!["user".to_string()],
};
let sname = PrincipalName {
name_type: 2,
name_string: vec!["krbtgt".to_string(), "EXAMPLE.COM".to_string()],
};
let encoded = encode_as_req(
&cname,
"EXAMPLE.COM",
&sname,
12345,
&[EncryptionType::Aes256CtsHmacSha196],
&[],
);
assert_eq!(encoded[0], 0x6a, "AS-REQ must start with APPLICATION [10]");
}
#[test]
fn test_encode_as_req_contains_pvno_and_msg_type() {
let cname = PrincipalName {
name_type: 1,
name_string: vec!["user".to_string()],
};
let sname = PrincipalName {
name_type: 2,
name_string: vec!["krbtgt".to_string(), "EXAMPLE.COM".to_string()],
};
let encoded = encode_as_req(
&cname,
"EXAMPLE.COM",
&sname,
12345,
&[EncryptionType::Aes256CtsHmacSha196],
&[],
);
let pvno_pattern = [0xa1, 0x03, 0x02, 0x01, 0x05];
assert!(
contains_subsequence(&encoded, &pvno_pattern),
"AS-REQ must contain pvno=5"
);
let msg_type_pattern = [0xa2, 0x03, 0x02, 0x01, 0x0a];
assert!(
contains_subsequence(&encoded, &msg_type_pattern),
"AS-REQ must contain msg-type=10"
);
}
#[test]
fn test_encode_tgs_req_application_tag() {
let sname = PrincipalName {
name_type: 2,
name_string: vec!["cifs".to_string(), "server.example.com".to_string()],
};
let fake_ap_req = vec![0x6e, 0x03, 0x01, 0x02, 0x03];
let encoded = encode_tgs_req(
"EXAMPLE.COM",
&sname,
54321,
&[EncryptionType::Aes256CtsHmacSha196],
&fake_ap_req,
);
assert_eq!(encoded[0], 0x6c, "TGS-REQ must start with APPLICATION [12]");
}
#[test]
fn test_encode_tgs_req_contains_msg_type_12() {
let sname = PrincipalName {
name_type: 2,
name_string: vec!["cifs".to_string(), "server.example.com".to_string()],
};
let fake_ap_req = vec![0x6e, 0x03, 0x01, 0x02, 0x03];
let encoded = encode_tgs_req(
"EXAMPLE.COM",
&sname,
54321,
&[EncryptionType::Aes256CtsHmacSha196],
&fake_ap_req,
);
let msg_type_pattern = [0xa2, 0x03, 0x02, 0x01, 0x0c];
assert!(
contains_subsequence(&encoded, &msg_type_pattern),
"TGS-REQ must contain msg-type=12"
);
}
#[test]
fn test_encode_ap_req_application_tag() {
let ticket = make_test_ticket();
let auth = EncryptedData {
etype: 18,
kvno: None,
cipher: vec![0xaa, 0xbb],
};
let encoded = encode_ap_req(&ticket, &auth, false);
assert_eq!(encoded[0], 0x6e, "AP-REQ must start with APPLICATION [14]");
}
#[test]
fn test_encode_ap_req_contains_pvno_and_msg_type() {
let ticket = make_test_ticket();
let auth = EncryptedData {
etype: 18,
kvno: None,
cipher: vec![0xaa, 0xbb],
};
let encoded = encode_ap_req(&ticket, &auth, false);
let pvno_pattern = [0xa0, 0x03, 0x02, 0x01, 0x05];
assert!(
contains_subsequence(&encoded, &pvno_pattern),
"AP-REQ must contain pvno=5"
);
let msg_type_pattern = [0xa1, 0x03, 0x02, 0x01, 0x0e];
assert!(
contains_subsequence(&encoded, &msg_type_pattern),
"AP-REQ must contain msg-type=14"
);
}
#[test]
fn test_encode_authenticator_application_tag() {
let cname = PrincipalName {
name_type: 1,
name_string: vec!["user".to_string()],
};
let encoded = encode_authenticator(
"EXAMPLE.COM",
&cname,
"20260408120000Z",
123456,
None,
None,
None,
);
assert_eq!(
encoded[0], 0x62,
"Authenticator must start with APPLICATION [2]"
);
}
#[test]
fn test_encode_authenticator_with_subkey() {
let cname = PrincipalName {
name_type: 1,
name_string: vec!["user".to_string()],
};
let subkey_value = vec![0x01; 32];
let encoded = encode_authenticator(
"EXAMPLE.COM",
&cname,
"20260408120000Z",
0,
Some((&subkey_value, 18)),
Some(42),
None,
);
assert_eq!(encoded[0], 0x62);
assert!(
contains_subsequence(&encoded, &[0xa6]),
"Authenticator with subkey must contain [6]"
);
assert!(
contains_subsequence(&encoded, &[0xa7]),
"Authenticator with seq-number must contain [7]"
);
}
#[test]
fn test_encode_pa_enc_timestamp() {
let encoded = encode_pa_enc_timestamp("20260408120000Z", 123456);
assert_eq!(encoded[0], TAG_SEQUENCE);
assert!(contains_subsequence(&encoded, &[0xa0]));
assert!(contains_subsequence(&encoded, &[0xa1]));
}
#[test]
fn test_parse_kdc_rep_as_rep() {
let rep_bytes = build_test_kdc_rep(11);
let rep = parse_kdc_rep(&rep_bytes).unwrap();
assert_eq!(rep.msg_type, 11);
assert_eq!(rep.crealm, "EXAMPLE.COM");
assert_eq!(rep.cname.name_type, 1);
assert_eq!(rep.cname.name_string, vec!["user"]);
assert_eq!(rep.ticket.realm, "EXAMPLE.COM");
assert_eq!(rep.enc_part.etype, 18);
}
#[test]
fn test_parse_kdc_rep_tgs_rep() {
let rep_bytes = build_test_kdc_rep(13);
let rep = parse_kdc_rep(&rep_bytes).unwrap();
assert_eq!(rep.msg_type, 13);
}
#[test]
fn test_parse_krb_error() {
let err_bytes = build_test_krb_error(25); let err = parse_krb_error(&err_bytes).unwrap();
assert_eq!(err.error_code, 25);
assert_eq!(err.realm, "EXAMPLE.COM");
assert_eq!(err.sname.name_type, 2);
assert_eq!(err.sname.name_string, vec!["krbtgt", "EXAMPLE.COM"]);
}
#[test]
fn test_parse_ticket_roundtrip() {
let ticket = make_test_ticket();
let encoded = encode_ticket(&ticket);
let parsed = parse_ticket(&encoded).unwrap();
assert_eq!(parsed.tkt_vno, 5);
assert_eq!(parsed.realm, "EXAMPLE.COM");
assert_eq!(parsed.sname.name_type, 2);
assert_eq!(parsed.sname.name_string, vec!["krbtgt", "EXAMPLE.COM"]);
assert_eq!(parsed.enc_part.etype, 18);
assert_eq!(parsed.enc_part.cipher, vec![0xde, 0xad, 0xbe, 0xef]);
}
#[test]
fn test_parse_enc_kdc_rep_part() {
let part_bytes = build_test_enc_kdc_rep_part();
let part = parse_enc_kdc_rep_part(&part_bytes).unwrap();
assert_eq!(part.key.keytype, 18);
assert_eq!(part.key.keyvalue, vec![0x01; 32]);
assert_eq!(part.nonce, 12345);
assert_eq!(part.authtime, "20260408120000Z");
assert_eq!(part.endtime, "20260409120000Z");
assert_eq!(part.srealm, "EXAMPLE.COM");
assert_eq!(part.sname.name_type, 2);
}
#[test]
fn test_principal_name_roundtrip() {
let name = PrincipalName {
name_type: 2,
name_string: vec!["cifs".to_string(), "server.example.com".to_string()],
};
let encoded = encode_principal_name(&name);
let parsed = parse_principal_name(&encoded).unwrap();
assert_eq!(parsed, name);
}
#[test]
fn test_encrypted_data_roundtrip() {
let ed = EncryptedData {
etype: 17,
kvno: Some(3),
cipher: vec![0x01, 0x02, 0x03, 0x04],
};
let encoded = encode_encrypted_data(&ed);
let parsed = parse_encrypted_data(&encoded).unwrap();
assert_eq!(parsed, ed);
}
#[test]
fn test_encrypted_data_no_kvno_roundtrip() {
let ed = EncryptedData {
etype: 23,
kvno: None,
cipher: vec![0xff; 16],
};
let encoded = encode_encrypted_data(&ed);
let parsed = parse_encrypted_data(&encoded).unwrap();
assert_eq!(parsed, ed);
}
#[test]
fn test_ticket_roundtrip() {
let ticket = make_test_ticket();
let encoded = encode_ticket(&ticket);
let parsed = parse_ticket(&encoded).unwrap();
assert_eq!(parsed.tkt_vno, ticket.tkt_vno);
assert_eq!(parsed.realm, ticket.realm);
assert_eq!(parsed.sname, ticket.sname);
assert_eq!(parsed.enc_part, ticket.enc_part);
assert!(parsed.raw_bytes.is_some());
assert_eq!(parsed.raw_bytes.as_ref().unwrap(), &encoded);
}
fn contains_subsequence(haystack: &[u8], needle: &[u8]) -> bool {
haystack
.windows(needle.len())
.any(|window| window == needle)
}
fn make_test_ticket() -> Ticket {
Ticket {
tkt_vno: 5,
realm: "EXAMPLE.COM".to_string(),
sname: PrincipalName {
name_type: 2,
name_string: vec!["krbtgt".to_string(), "EXAMPLE.COM".to_string()],
},
enc_part: EncryptedData {
etype: 18,
kvno: Some(2),
cipher: vec![0xde, 0xad, 0xbe, 0xef],
},
raw_bytes: None,
}
}
fn build_test_kdc_rep(msg_type_val: i32) -> Vec<u8> {
let pvno = der_context(0, &der_integer(5));
let msg_type = der_context(1, &der_integer(msg_type_val));
let crealm = der_context(3, &der_general_string("EXAMPLE.COM"));
let cname_inner = encode_principal_name(&PrincipalName {
name_type: 1,
name_string: vec!["user".to_string()],
});
let cname = der_context(4, &cname_inner);
let ticket = der_context(5, &encode_ticket(&make_test_ticket()));
let enc_part_inner = encode_encrypted_data(&EncryptedData {
etype: 18,
kvno: Some(1),
cipher: vec![0xca, 0xfe],
});
let enc_part = der_context(6, &enc_part_inner);
let seq = der_sequence(&[&pvno, &msg_type, &crealm, &cname, &ticket, &enc_part]);
let app_tag = match msg_type_val {
11 => 11, 13 => 13, _ => panic!("unexpected msg_type_val"),
};
der_application(app_tag, &seq)
}
fn build_test_krb_error(error_code_val: i32) -> Vec<u8> {
let pvno = der_context(0, &der_integer(5));
let msg_type = der_context(1, &der_integer(30));
let stime = der_context(4, &der_generalized_time("20260408120000Z"));
let susec = der_context(5, &der_integer(0));
let error_code = der_context(6, &der_integer(error_code_val));
let realm = der_context(9, &der_general_string("EXAMPLE.COM"));
let sname_inner = encode_principal_name(&PrincipalName {
name_type: 2,
name_string: vec!["krbtgt".to_string(), "EXAMPLE.COM".to_string()],
});
let sname = der_context(10, &sname_inner);
let seq = der_sequence(&[
&pvno,
&msg_type,
&stime,
&susec,
&error_code,
&realm,
&sname,
]);
der_application(30, &seq)
}
fn build_test_enc_kdc_rep_part() -> Vec<u8> {
let kt = der_context(0, &der_integer(18));
let kv = der_context(1, &der_octet_string(&[0x01; 32]));
let key_seq = der_sequence(&[&kt, &kv]);
let key = der_context(0, &key_seq);
let last_req = der_context(1, &der_sequence(&[]));
let nonce = der_context(2, &der_integer_u32(12345));
let flags = der_context(4, &der_bit_string(&[0x50, 0x80, 0x00, 0x00], 0));
let authtime = der_context(5, &der_generalized_time("20260408120000Z"));
let endtime = der_context(7, &der_generalized_time("20260409120000Z"));
let srealm = der_context(9, &der_general_string("EXAMPLE.COM"));
let sname_inner = encode_principal_name(&PrincipalName {
name_type: 2,
name_string: vec!["krbtgt".to_string(), "EXAMPLE.COM".to_string()],
});
let sname = der_context(10, &sname_inner);
let seq = der_sequence(&[
&key, &last_req, &nonce, &flags, &authtime, &endtime, &srealm, &sname,
]);
der_application(25, &seq)
}
}