const OID_SPNEGO: [u8; 6] = [0x2b, 0x06, 0x01, 0x05, 0x05, 0x02];
const OID_NTLMSSP: [u8; 10] = [0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a];
fn der_len(n: usize) -> Vec<u8> {
if n < 0x80 {
vec![n as u8]
} else {
let mut body = Vec::new();
let mut v = n;
while v > 0 {
body.insert(0, (v & 0xff) as u8);
v >>= 8;
}
let mut out = vec![0x80 | body.len() as u8];
out.extend(body);
out
}
}
fn tlv(tag: u8, value: &[u8]) -> Vec<u8> {
let mut out = vec![tag];
out.extend(der_len(value.len()));
out.extend_from_slice(value);
out
}
pub fn negotiate_init(ntlm_type1: &[u8]) -> Vec<u8> {
let mech_types = tlv(0xa0, &tlv(0x30, &tlv(0x06, &OID_NTLMSSP))); let mech_token = tlv(0xa2, &tlv(0x04, ntlm_type1)); let mut inner = mech_types;
inner.extend(mech_token);
let neg_token_init = tlv(0xa0, &tlv(0x30, &inner)); let mut gss = tlv(0x06, &OID_SPNEGO);
gss.extend(neg_token_init);
tlv(0x60, &gss) }
pub fn negotiate_resp(ntlm_type3: &[u8]) -> Vec<u8> {
let response_token = tlv(0xa2, &tlv(0x04, ntlm_type3)); tlv(0xa1, &tlv(0x30, &response_token)) }
pub fn challenge_resp(ntlm_type2: &[u8]) -> Vec<u8> {
let neg_state = tlv(0xa0, &tlv(0x0a, &[0x01])); let supported_mech = tlv(0xa1, &tlv(0x06, &OID_NTLMSSP)); let response_token = tlv(0xa2, &tlv(0x04, ntlm_type2)); let mut inner = neg_state;
inner.extend(supported_mech);
inner.extend(response_token);
tlv(0xa1, &tlv(0x30, &inner)) }
pub fn find_ntlm(buf: &[u8]) -> Option<&[u8]> {
buf.windows(8)
.position(|w| w == b"NTLMSSP\0")
.map(|i| &buf[i..])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn init_is_wellformed_and_embeds_token() {
let t1 = b"NTLMSSP\0aaaa";
let w = negotiate_init(t1);
assert_eq!(w[0], 0x60); assert_eq!(find_ntlm(&w), Some(&t1[..]));
}
#[test]
fn long_len_encoding() {
assert_eq!(der_len(0x7f), vec![0x7f]);
assert_eq!(der_len(0x80), vec![0x81, 0x80]);
assert_eq!(der_len(0x1234), vec![0x82, 0x12, 0x34]);
}
}