ntlmclient/
lib.rs

1//! A simple NTLM client library for Rust.
2//!
3//! Sample usage:
4//! ```
5//! use base64::prelude::{BASE64_STANDARD, Engine};
6//!
7//! const EWS_URL: &str = "https://example.com/EWS/Exchange.asmx";
8//!
9//! async fn initialize_authed_client(username: &str, password: &str, domain: &str, local_hostname: &str) -> reqwest::Client {
10//!     let nego_flags
11//!         = ntlmclient::Flags::NEGOTIATE_UNICODE
12//!         | ntlmclient::Flags::REQUEST_TARGET
13//!         | ntlmclient::Flags::NEGOTIATE_NTLM
14//!         | ntlmclient::Flags::NEGOTIATE_WORKSTATION_SUPPLIED
15//!         ;
16//!     let nego_msg = ntlmclient::Message::Negotiate(ntlmclient::NegotiateMessage {
17//!         flags: nego_flags,
18//!         supplied_domain: String::new(),
19//!         supplied_workstation: local_hostname.to_owned(),
20//!         os_version: Default::default(),
21//!     });
22//!     let nego_msg_bytes = nego_msg.to_bytes()
23//!         .expect("failed to encode NTLM negotiation message");
24//!     let nego_b64 = BASE64_STANDARD.encode(&nego_msg_bytes);
25//!
26//!     let client = reqwest::Client::builder()
27//!         .cookie_store(true)
28//!         .build()
29//!         .expect("failed to build client");
30//!     let resp = client.get(EWS_URL)
31//!         .header("Authorization", format!("NTLM {}", nego_b64))
32//!         .send().await
33//!         .expect("failed to send challenge request to Exchange");
34//!     let challenge_header = resp.headers().get("www-authenticate")
35//!         .expect("response missing challenge header");
36//!
37//!     // we might have been redirected to a specialized authentication URL
38//!     let auth_url = resp.url();
39//!
40//!     let challenge_b64 = challenge_header.to_str()
41//!         .expect("challenge header not a string")
42//!         .split(" ")
43//!         .nth(1).expect("second chunk of challenge header missing");
44//!     let challenge_bytes = BASE64_STANDARD.decode(challenge_b64)
45//!         .expect("base64 decoding challenge message failed");
46//!     let challenge = ntlmclient::Message::try_from(challenge_bytes.as_slice())
47//!         .expect("decoding challenge message failed");
48//!     let challenge_content = match challenge {
49//!         ntlmclient::Message::Challenge(c) => c,
50//!         other => panic!("wrong challenge message: {:?}", other),
51//!     };
52//!     let target_info_bytes: Vec<u8> = challenge_content.target_information
53//!         .iter()
54//!         .flat_map(|ie| ie.to_bytes())
55//!         .collect();
56//!
57//!     // calculate the response
58//!     let creds = ntlmclient::Credentials {
59//!         username: username.to_owned(),
60//!         password: password.to_owned(),
61//!         domain: domain.to_owned(),
62//!     };
63//!     let challenge_response = ntlmclient::respond_challenge_ntlm_v2(
64//!         challenge_content.challenge,
65//!         &target_info_bytes,
66//!         ntlmclient::get_ntlm_time(),
67//!         &creds,
68//!     );
69//!
70//!     // assemble the packet
71//!     let auth_flags
72//!         = ntlmclient::Flags::NEGOTIATE_UNICODE
73//!         | ntlmclient::Flags::NEGOTIATE_NTLM
74//!         ;
75//!     let auth_msg = challenge_response.to_message(
76//!         &creds,
77//!         local_hostname,
78//!         auth_flags,
79//!     );
80//!     let auth_msg_bytes = auth_msg.to_bytes()
81//!         .expect("failed to encode NTLM authentication message");
82//!     let auth_b64 = BASE64_STANDARD.encode(&auth_msg_bytes);
83//!
84//!     client.get(auth_url.clone())
85//!         .header("Authorization", format!("NTLM {}", auth_b64))
86//!         .send().await
87//!         .expect("failed to send authentication request to Exchange")
88//!         .error_for_status()
89//!         .expect("error response to authentication message");
90//!
91//!     // try calling again, without the auth stuff (thanks to cookies)
92//!     client.get(EWS_URL)
93//!         .send().await
94//!         .expect("failed to send refresher request to Exchange")
95//!         .error_for_status()
96//!         .expect("error response to refresher message");
97//!
98//!     client
99//! }
100//! ```
101
102
103#[cfg(windows)]
104mod encoding_windows;
105
106#[cfg(not(windows))]
107mod encoding_utf8;
108
109
110use std::fmt;
111
112use bitflags::bitflags;
113use chrono::{NaiveDate, Utc};
114use cipher::{BlockEncrypt, KeyInit};
115use cipher::generic_array::GenericArray;
116use cipher::generic_array::typenum::U8;
117use des::Des;
118use digest::Digest;
119use hmac::{Hmac, Mac};
120use md4::Md4;
121use md5::Md5;
122use rand::Rng;
123use rand::rngs::OsRng;
124
125#[cfg(windows)]
126use crate::encoding_windows::{ansi_string_to_rust, rust_string_to_ansi};
127
128#[cfg(not(windows))]
129use crate::encoding_utf8::{ansi_string_to_rust, rust_string_to_ansi};
130
131
132/// The magic value at the start of every NTLMSSP data packet.
133const NTLMSSP_MAGIC: [u8; 8] = *b"NTLMSSP\0";
134
135
136/// Standard NTLM credentials, consisting of username, password and domain.
137#[derive(Clone, Debug, Eq, Hash, PartialEq)]
138pub struct Credentials {
139    /// The username part of the credentials.
140    pub username: String,
141
142    /// The password part of the credentials.
143    pub password: String,
144
145    /// The domain part of the credentials.
146    ///
147    /// Often specified in combination with the username as `<DOMAIN>\<USERNAME>`. In credentials
148    /// without a domain, the domain is an empty string.
149    pub domain: String,
150}
151
152/// The response to an NTLM challenge.
153#[derive(Clone, Debug, Eq, Hash, PartialEq)]
154pub struct ChallengeResponse {
155    /// The classic LanManager (LM) response.
156    pub lm_response: Vec<u8>,
157
158    /// The NT LanManager (NTLM) response.
159    pub ntlm_response: Vec<u8>,
160
161    /// The session key, generally calculated from elements of the responses.
162    pub session_key: Vec<u8>,
163}
164
165
166bitflags! {
167    /// NTLM operation flags.
168    #[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
169    pub struct Flags: u32 {
170        const NEGOTIATE_UNICODE = 0x0000_0001;
171        const NEGOTIATE_OEM = 0x0000_0002;
172        const REQUEST_TARGET = 0x0000_0004;
173        const UNKNOWN_8 = 0x0000_0008;
174        const NEGOTIATE_SIGN = 0x0000_0010;
175        const NEGOTIATE_SEAL = 0x0000_0020;
176        const NEGOTIATE_DATAGRAM = 0x0000_0040;
177        const NEGOTIATE_LANMAN_KEY = 0x0000_0080;
178        const NEGOTIATE_NETWARE = 0x0000_0100;
179        const NEGOTIATE_NTLM = 0x0000_0200;
180        const UNKNOWN_400 = 0x0000_0400;
181        const NEGOTIATE_ANONYMOUS = 0x0000_0800;
182        const NEGOTIATE_DOMAIN_SUPPLIED = 0x0000_1000;
183        const NEGOTIATE_WORKSTATION_SUPPLIED = 0x0000_2000;
184        const NEGOTIATE_LOCAL_CALL = 0x0000_4000;
185        const NEGOTIATE_ALWAYS_SIGN = 0x0000_8000;
186        const TARGET_TYPE_DOMAIN = 0x0001_0000;
187        const TARGET_TYPE_SERVER = 0x0002_0000;
188        const TARGET_TYPE_SHARE = 0x0004_0000;
189        const NEGOTIATE_NTLM2_KEY = 0x0008_0000;
190        const REQUEST_INIT_RESPONSE = 0x0010_0000;
191        const REQUEST_ACCEPT_RESPONSE = 0x0020_0000;
192        const REQUEST_NON_NT_SESSION_KEY = 0x0040_0000;
193        const NEGOTIATE_TARGET_INFO = 0x0080_0000;
194        const UNKNOWN_1000000 = 0x0100_0000;
195        const NEGOTIATE_VERSION = 0x0200_0000;
196        const UNKNOWN_4000000 = 0x0400_0000;
197        const UNKNOWN_8000000 = 0x0800_0000;
198        const UNKNOWN_10000000 = 0x1000_0000;
199        const NEGOTIATE_128BIT = 0x2000_0000;
200        const NEGOTIATE_KEY_EXCHANGE = 0x4000_0000;
201        const NEGOTIATE_56BIT = 0x8000_0000;
202    }
203}
204
205
206/// An error that may occur while parsing existing NTLM packets.
207#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
208pub enum ParsingError {
209    /// The header is shorter than expected.
210    ShortHeader { expected_min_len: usize, obtained_len: usize },
211
212    /// The magic value does not match the expected one.
213    MagicMismatch { expected: [u8; 8], obtained: Vec<u8> },
214
215    /// An internal item has a different length than expected.
216    ItemLengthMismatch { expected: usize, obtained: usize },
217
218    /// An internal item is shorter than expected.
219    ItemMinLengthMismatch { expected_at_least: usize, obtained: usize },
220
221    /// An internal item's length is not divisible by an expected divisor.
222    ItemLengthNotDivisible { expected_divisor: usize, obtained_length: usize },
223
224    /// A byte string cannot be decoded using the current OEM encoding.
225    InvalidOemEncoding { value: Vec<u8> },
226
227    /// A string of 16-bit characters could not be decoded as UTF-8.
228    InvalidUtf16 { value: Vec<u16> },
229
230    /// An offset is too large for the isize type.
231    OffsetTooLargeIsize,
232
233    /// A length is too large for the isize type.
234    LengthTooLargeIsize,
235
236    /// A start value is out of the range of a slice.
237    StartOutOfRange { start: isize, length: usize },
238
239    /// An end value is out of the range of a slice.
240    EndOutOfRange { end: isize, length: usize },
241
242    /// Neither Unicode nor OEM encoding was selected.
243    NeitherUnicodeNorOem,
244}
245impl fmt::Display for ParsingError {
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        match self {
248            Self::ShortHeader { expected_min_len, obtained_len }
249                => write!(f, "header too short (expected at least {} bytes, obtained {})", expected_min_len, obtained_len),
250            Self::MagicMismatch { expected, obtained }
251                => write!(f, "mismatched magic (expected {:?}, obtained {:?})", expected, obtained),
252            Self::ItemLengthMismatch { expected, obtained }
253                => write!(f, "insufficient length for an internal item (expected {:?}, obtained {:?})", expected, obtained),
254            Self::ItemMinLengthMismatch { expected_at_least, obtained }
255                => write!(f, "insufficient minimum length for an internal item (expected at least {:?}, obtained {:?})", expected_at_least, obtained),
256            Self::ItemLengthNotDivisible { expected_divisor, obtained_length }
257                => write!(f, "item length {} not divisible by {}", obtained_length, expected_divisor),
258            Self::InvalidOemEncoding { value }
259                => write!(f, "failed to decode value with the current OEM encoding: {:?}", value),
260            Self::InvalidUtf16{ value }
261                => write!(f, "failed to decode value as UTF-16: {:?}", value),
262            Self::OffsetTooLargeIsize
263                => write!(f, "the offset value is too large for the isize type"),
264            Self::LengthTooLargeIsize
265                => write!(f, "the length value is too large for the isize type"),
266            Self::StartOutOfRange { start, length }
267                => write!(f, "start ({}) out of range (slice has {} items)", start, length),
268            Self::EndOutOfRange { end, length }
269                => write!(f, "end ({}) out of range (slice has {} items)", end, length),
270            Self::NeitherUnicodeNorOem
271                => write!(f, "neither Unicode nor OEM encoding was selected"),
272        }
273    }
274}
275impl std::error::Error for ParsingError {
276}
277
278/// An error that may occur while writing an NTLM packet.
279#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
280pub enum StoringError {
281    /// The string cannot be encoded using the OEM encoding.
282    NonOemEncodable { string: String },
283
284    /// Neither Unicode nor OEM encoding was selected.
285    NeitherUnicodeNorOem,
286}
287impl fmt::Display for StoringError {
288    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289        match self {
290            Self::NonOemEncodable { string }
291                => write!(f, "failed to encode {:?} using OEM encoding", string),
292            Self::NeitherUnicodeNorOem
293                => write!(f, "neither Unicode nor OEM encoding was selected"),
294        }
295    }
296}
297impl std::error::Error for StoringError {
298}
299
300/// An NTLM message.
301#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
302pub enum Message {
303    Negotiate(NegotiateMessage),
304    Challenge(ChallengeMessage),
305    Authenticate(AuthenticateMessage),
306    Other(u32, Vec<u8>),
307}
308impl Message {
309    /// Returns the 32-bit message number identifying the type of this message.
310    pub fn message_number(&self) -> u32 {
311        match self {
312            Self::Negotiate(_) => 0x0000_0001,
313            Self::Challenge(_) => 0x0000_0002,
314            Self::Authenticate(_) => 0x0000_0003,
315            Self::Other(t, _data) => *t,
316        }
317    }
318}
319
320/// A structure representing the version of an operating system as well as the NTLM revision used.
321#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
322pub struct OsVersion {
323    pub major_version: u8,
324    pub minor_version: u8,
325    pub build_number: u16,
326    pub reserved: [u8; 3],
327    pub ntlm_revision: u8,
328}
329impl Default for OsVersion {
330    fn default() -> Self {
331        Self {
332            major_version: 0,
333            minor_version: 0,
334            build_number: 0,
335            reserved: [0, 0, 0],
336            ntlm_revision: 0,
337        }
338    }
339}
340
341/// The contents of an NTLM Negotiate message.
342///
343/// The Negotiate message is the first message in an NTLM challenge-response process and is sent by
344/// the client to the server; the server is expected to respond with a Challenge message.
345#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
346pub struct NegotiateMessage {
347    /// Stores which information has been specified and which NTLM behavior should be negotiated.
348    pub flags: Flags,
349
350    /// The domain against which the client wishes to authenticate.
351    pub supplied_domain: String,
352
353    /// The NT hostname of the client.
354    pub supplied_workstation: String,
355
356    /// Version information about the client's operating system.
357    pub os_version: OsVersion,
358}
359
360/// The contents of an NTLM Challenge message.
361///
362/// The Challenge message is sent by the server in response to the client's Negotiate message; the
363/// client is expected to respond with an Authenticate message.
364#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
365pub struct ChallengeMessage {
366    /// The host against which the client is authenticating.
367    pub target_name: String,
368
369    /// Stores which NTLM behavior has been accepted by the server from the client's request.
370    pub flags: Flags,
371
372    /// The challenge value.
373    pub challenge: [u8; 8],
374
375    /// The context value.
376    pub context: (u32, u32),
377
378    /// Information about the targets of the authentication.
379    pub target_information: Vec<TargetInfoEntry>,
380
381    /// Version information about the server's operating system.
382    pub os_version: OsVersion,
383}
384
385/// The contents of an NTLM Authenticate message.
386///
387/// The Authenticate message is sent by the client in response to the server's Challenge message;
388/// once it is accepted by the server, the authentication has succeeded.
389#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
390pub struct AuthenticateMessage {
391    pub lm_response: Vec<u8>,
392    pub ntlm_response: Vec<u8>,
393    pub domain_name: String,
394    pub user_name: String,
395    pub workstation_name: String,
396    pub session_key: Vec<u8>,
397    pub flags: Flags,
398    pub os_version: OsVersion,
399}
400
401/// An NTLM security buffer, pointing to a string contained later in the message.
402#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
403pub struct SecurityBuffer {
404    pub length: u16,
405    pub capacity: u16,
406    pub offset: u32,
407}
408
409/// The type of additional target information included in the Challenge message.
410#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
411pub enum TargetInfoType {
412    Terminator,
413    NtServer,
414    NtDomain,
415    DnsDomain,
416    DnsServer,
417    DnsForest,
418    Flags,
419    Timestamp,
420    SingleHost,
421    TargetName,
422    ChannelBindings,
423    Unknown(u16),
424}
425impl From<TargetInfoType> for u16 {
426    fn from(t: TargetInfoType) -> Self {
427        match t {
428            TargetInfoType::Terminator => 0x0000,
429            TargetInfoType::NtServer => 0x0001,
430            TargetInfoType::NtDomain => 0x0002,
431            TargetInfoType::DnsServer => 0x0003,
432            TargetInfoType::DnsDomain => 0x0004,
433            TargetInfoType::DnsForest => 0x0005,
434            TargetInfoType::Flags => 0x0006,
435            TargetInfoType::Timestamp => 0x0007,
436            TargetInfoType::SingleHost => 0x0008,
437            TargetInfoType::TargetName => 0x0009,
438            TargetInfoType::ChannelBindings => 0x000A,
439            TargetInfoType::Unknown(w) => w,
440        }
441    }
442}
443impl From<u16> for TargetInfoType {
444    fn from(w: u16) -> Self {
445        match w {
446            0x0000 => TargetInfoType::Terminator,
447            0x0001 => TargetInfoType::NtServer,
448            0x0002 => TargetInfoType::NtDomain,
449            0x0003 => TargetInfoType::DnsServer,
450            0x0004 => TargetInfoType::DnsDomain,
451            0x0005 => TargetInfoType::DnsForest,
452            0x0006 => TargetInfoType::Flags,
453            0x0007 => TargetInfoType::Timestamp,
454            0x0008 => TargetInfoType::SingleHost,
455            0x0009 => TargetInfoType::TargetName,
456            0x000A => TargetInfoType::ChannelBindings,
457            other => TargetInfoType::Unknown(other),
458        }
459    }
460}
461
462/// An entry of additional target information included in the Challenge message.
463#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
464pub struct TargetInfoEntry {
465    pub entry_type: TargetInfoType,
466    pub data: Vec<u8>,
467}
468
469
470// serialization and deserialization code
471
472
473/// Appends a security buffer to the end of a message.
474///
475/// The security buffer is appended to `message_bytes` while the data itself is appended to
476/// `data_block`. It is assumed that once `message_bytes` has been filled with all the required
477/// information, the contents of `data_block` are appended to it. `sec_buffer_offset` takes care of
478/// the next free offset in the message at which data of a security buffer can be appended.
479fn append_sec_buffer(message_bytes: &mut Vec<u8>, data_block: &mut Vec<u8>, sec_buffer_offset: &mut u32, data: &[u8]) {
480    // data_block will be placed at the end of the packet; fill it with the actual data
481    data_block.extend_from_slice(data);
482
483    // create an NTLM security buffer which will point to the data
484    let mut sb = SecurityBuffer::for_slice(data);
485    sb.offset = *sec_buffer_offset;
486
487    // add this security buffer to the message
488    message_bytes.extend_from_slice(&sb.to_bytes());
489
490    // update the offset for the next security buffer
491    *sec_buffer_offset += u32::from(sb.length);
492}
493
494/// Appends a security buffer to the end of a message, where the data is a string.
495///
496/// Functions similarly to [`append_sec_buffer`], but encodes the string in the expected format
497/// first.
498fn append_sec_buffer_string(packet_bytes: &mut Vec<u8>, data_block: &mut Vec<u8>, sec_buffer_offset: &mut u32, flags: Flags, data: &str) -> Result<(), StoringError> {
499    let bs = if flags.contains(Flags::NEGOTIATE_UNICODE) {
500        data.encode_utf16()
501            .flat_map(|w| w.to_le_bytes())
502            .collect()
503    } else if flags.contains(Flags::NEGOTIATE_OEM) {
504        rust_string_to_ansi(data)
505            .ok_or_else(|| StoringError::NonOemEncodable { string: data.to_owned() })?
506    } else {
507        return Err(StoringError::NeitherUnicodeNorOem);
508    };
509
510    append_sec_buffer(packet_bytes, data_block, sec_buffer_offset, &bs);
511
512    Ok(())
513}
514
515/// Converts UTF-16 values stored as bytes in little-endian format into a string.
516fn utf16_le_bytes_to_string(bytes: &[u8]) -> Result<String, ParsingError> {
517    if bytes.len() % 2 != 0 {
518        return Err(ParsingError::ItemLengthNotDivisible { expected_divisor: 2, obtained_length: bytes.len() });
519    }
520    let u16s: Vec<u16> = bytes.chunks_exact(2)
521        .map(|chk| u16::from_le_bytes(chk.try_into().unwrap()))
522        .collect();
523    String::from_utf16(&u16s)
524        .or(Err(ParsingError::InvalidUtf16{ value: u16s }))
525}
526
527/// Converts an ANSI string into a Rust string.
528fn oem_bytes_to_string(bytes: &[u8]) -> Result<String, ParsingError> {
529    ansi_string_to_rust(bytes)
530        .ok_or_else(|| ParsingError::InvalidOemEncoding { value: Vec::from(bytes) })
531}
532
533/// Converts a string into a Rust string, using ANSI or UTF-16 encoding depending on the `flags`.
534fn ntlm_bytes_to_string(flags: Flags, bytes: &[u8]) -> Result<String, ParsingError> {
535    if flags.contains(Flags::NEGOTIATE_UNICODE) {
536        utf16_le_bytes_to_string(bytes)
537    } else if flags.contains(Flags::NEGOTIATE_OEM) {
538        oem_bytes_to_string(bytes)
539    } else {
540        Err(ParsingError::NeitherUnicodeNorOem)
541    }
542}
543
544impl Message {
545    /// Serializes the NTLM message into bytes.
546    pub fn to_bytes(&self) -> Result<Vec<u8>, StoringError> {
547        let mut buf = Vec::new();
548        buf.extend_from_slice(&NTLMSSP_MAGIC);
549        buf.extend_from_slice(&self.message_number().to_le_bytes());
550        match self {
551            Message::Negotiate(t1m) => {
552                buf.extend_from_slice(&t1m.to_bytes()?);
553            },
554            Message::Challenge(t2m) => {
555                buf.extend_from_slice(&t2m.to_bytes()?);
556            },
557            Message::Authenticate(t3m) => {
558                buf.extend_from_slice(&t3m.to_bytes()?);
559            },
560            Message::Other(_msg_num, data) => {
561                buf.extend_from_slice(data);
562            }
563        }
564        Ok(buf)
565    }
566}
567impl TryFrom<&[u8]> for Message {
568    type Error = ParsingError;
569
570    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
571        if value.len() < 12 {
572            // assume magic mismatch
573            return Err(ParsingError::ShortHeader { expected_min_len: 12, obtained_len: value.len() });
574        }
575        let obtained_magic: [u8; 8] = value[0..8].try_into().unwrap();
576        if obtained_magic != NTLMSSP_MAGIC {
577            return Err(ParsingError::MagicMismatch { expected: NTLMSSP_MAGIC, obtained: Vec::from(obtained_magic) });
578        }
579        let message_type = u32::from_le_bytes(value[8..12].try_into().unwrap());
580        match message_type {
581            0x0000_0001 => NegotiateMessage::try_from(&value[12..])
582                .map(|t1m| Message::Negotiate(t1m)),
583            0x0000_0002 => ChallengeMessage::try_from(&value[12..])
584                .map(|t2m| Message::Challenge(t2m)),
585            0x0000_0003 => AuthenticateMessage::try_from(&value[12..])
586                .map(|t3m| Message::Authenticate(t3m)),
587            other_type => Ok(Message::Other(other_type, Vec::from(&value[12..]))),
588        }
589    }
590}
591
592impl OsVersion {
593    /// Serializes the OS version structure into bytes.
594    pub fn to_bytes(&self) -> Vec<u8> {
595        let mut ret = Vec::with_capacity(8);
596        ret.push(self.major_version);
597        ret.push(self.minor_version);
598        ret.extend_from_slice(&self.build_number.to_le_bytes());
599        ret.extend_from_slice(&self.reserved);
600        ret.push(self.ntlm_revision);
601        ret
602    }
603}
604impl TryFrom<&[u8]> for OsVersion {
605    type Error = ParsingError;
606
607    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
608        if value.len() != 8 {
609            return Err(ParsingError::ItemLengthMismatch { expected: 8, obtained: value.len() });
610        }
611
612        let major_version = value[0];
613        let minor_version = value[1];
614        let build_number = u16::from_le_bytes(value[2..4].try_into().unwrap());
615        let reserved = value[4..7].try_into().unwrap();
616        let ntlm_revision = value[7];
617
618        Ok(OsVersion {
619            major_version,
620            minor_version,
621            build_number,
622            reserved,
623            ntlm_revision,
624        })
625    }
626}
627
628impl NegotiateMessage {
629    /// Serializes the Negotiate message body into bytes.
630    pub fn to_bytes(&self) -> Result<Vec<u8>, StoringError> {
631        let mut sec_buffer_offset: u32
632            = 8 // magic
633            + 4 // message type
634            + 4 // flags
635            + 8 // supplied domain secbuffer
636            + 8 // supplied workstation secbuffer
637            + 8 // version
638            ;
639
640        let mut ret = Vec::new();
641        let mut data_block = Vec::new();
642
643        ret.extend_from_slice(&self.flags.bits().to_le_bytes());
644        append_sec_buffer_string(&mut ret, &mut data_block, &mut sec_buffer_offset, self.flags, &self.supplied_domain)?;
645        append_sec_buffer_string(&mut ret, &mut data_block, &mut sec_buffer_offset, self.flags, &self.supplied_workstation)?;
646        ret.extend_from_slice(&self.os_version.to_bytes());
647        ret.append(&mut data_block);
648        Ok(ret)
649    }
650}
651impl TryFrom<&[u8]> for NegotiateMessage {
652    type Error = ParsingError;
653
654    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
655        // magic and message type have already been sliced away
656
657        if value.len() < 32 {
658            return Err(ParsingError::ItemMinLengthMismatch { expected_at_least: 32, obtained: value.len() });
659        }
660        let flags_u32 = u32::from_le_bytes(value[0..4].try_into().unwrap());
661        let flags = Flags::from_bits(flags_u32).unwrap();
662
663        let supplied_domain_secbuf = SecurityBuffer::try_from(&value[4..12]).unwrap();
664        let supplied_workstation_secbuf = SecurityBuffer::try_from(&value[12..20]).unwrap();
665        let os_version = if flags.contains(Flags::NEGOTIATE_VERSION) {
666            OsVersion::try_from(&value[20..28]).unwrap()
667        } else {
668            OsVersion::default()
669        };
670
671        // offsets in secbufs are in relation to the start of the message
672        // however, magic and message type have already been sliced away
673        // adjust offsets accordingly
674        let supplied_domain_bytes = supplied_domain_secbuf.apply_to_slice(&value, -(8 + 4))?;
675        let supplied_workstation_bytes = supplied_workstation_secbuf.apply_to_slice(&value, -(8 + 4))?;
676
677        let supplied_domain = ntlm_bytes_to_string(flags, supplied_domain_bytes)?;
678        let supplied_workstation = ntlm_bytes_to_string(flags, supplied_workstation_bytes)?;
679
680        Ok(Self {
681            flags,
682            supplied_domain,
683            supplied_workstation,
684            os_version,
685        })
686    }
687}
688
689impl ChallengeMessage {
690    /// Serializes the Challenge message body into bytes.
691    pub fn to_bytes(&self) -> Result<Vec<u8>, StoringError> {
692        let mut sec_buffer_offset: u32
693            = 8 // magic
694            + 4 // message type
695            + 8 // target name secbuffer
696            + 4 // flags
697            + 8 // challenge
698            + 8 // context
699            + 8 // target information secbuffer
700            + 8 // version
701            ;
702
703        let mut ret = Vec::new();
704        let mut data_block = Vec::new();
705
706        append_sec_buffer_string(&mut ret, &mut data_block, &mut sec_buffer_offset, self.flags, &self.target_name)?;
707        ret.extend_from_slice(&self.flags.bits().to_le_bytes());
708        ret.extend_from_slice(&self.challenge);
709        data_block.extend_from_slice(&self.context.0.to_le_bytes());
710        data_block.extend_from_slice(&self.context.1.to_le_bytes());
711        {
712            let target_info_bytes: Vec<u8> = self.target_information.iter()
713                .flat_map(|ti| ti.to_bytes())
714                .collect();
715            append_sec_buffer(&mut ret, &mut data_block, &mut sec_buffer_offset, &target_info_bytes);
716        }
717        ret.extend_from_slice(&self.os_version.to_bytes());
718        ret.append(&mut data_block);
719        Ok(ret)
720    }
721}
722impl TryFrom<&[u8]> for ChallengeMessage {
723    type Error = ParsingError;
724
725    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
726        // magic and message type have already been sliced away
727
728        if value.len() < 44 {
729            return Err(ParsingError::ItemMinLengthMismatch { expected_at_least: 44, obtained: value.len() });
730        }
731        let target_name_secbuf = SecurityBuffer::try_from(&value[0..8]).unwrap();
732        let flags_u32 = u32::from_le_bytes(value[8..12].try_into().unwrap());
733        let flags = Flags::from_bits(flags_u32).unwrap();
734        let challenge = value[12..20].try_into().unwrap();
735        let context = {
736            let context_0 = u32::from_le_bytes(value[20..24].try_into().unwrap());
737            let context_1 = u32::from_le_bytes(value[24..28].try_into().unwrap());
738            (context_0, context_1)
739        };
740        let target_info_secbuf = SecurityBuffer::try_from(&value[28..36]).unwrap();
741        let os_version = if flags.contains(Flags::NEGOTIATE_VERSION) {
742            OsVersion::try_from(&value[36..44]).unwrap()
743        } else {
744            OsVersion::default()
745        };
746
747        // offsets in secbufs are in relation to the start of the message
748        // however, magic and message type have already been sliced away
749        // adjust offsets accordingly
750        let target_name_bytes = target_name_secbuf.apply_to_slice(&value, -(8 + 4))?;
751        let mut target_info_bytes = target_info_secbuf.apply_to_slice(&value, -(8 + 4))?;
752
753        let target_name = ntlm_bytes_to_string(flags, target_name_bytes)?;
754
755        let mut target_information = Vec::new();
756        while target_info_bytes.len() > 0 {
757            let (tie, next) = TargetInfoEntry::try_from_bytes(&target_info_bytes)?;
758            target_information.push(tie);
759            target_info_bytes = next;
760        }
761
762        Ok(Self {
763            target_name,
764            flags,
765            challenge,
766            context,
767            target_information,
768            os_version,
769        })
770    }
771}
772
773impl AuthenticateMessage {
774    /// Serializes the Authenticate message body into bytes.
775    pub fn to_bytes(&self) -> Result<Vec<u8>, StoringError> {
776        let mut sec_buffer_offset: u32
777            = 8 // magic
778            + 4 // message type
779            + 8 // LM response secbuffer
780            + 8 // NTLM response secbuffer
781            + 8 // domain name secbuffer
782            + 8 // user name secbuffer
783            + 8 // workstation name secbuffer
784            + 8 // session key secbuffer
785            + 4 // flags
786            + 8 // version
787            ;
788
789        let mut ret = Vec::new();
790        let mut data_block = Vec::new();
791
792        append_sec_buffer(&mut ret, &mut data_block, &mut sec_buffer_offset, &self.lm_response);
793        append_sec_buffer(&mut ret, &mut data_block, &mut sec_buffer_offset, &self.ntlm_response);
794        append_sec_buffer_string(&mut ret, &mut data_block, &mut sec_buffer_offset, self.flags, &self.domain_name)?;
795        append_sec_buffer_string(&mut ret, &mut data_block, &mut sec_buffer_offset, self.flags, &self.user_name)?;
796        append_sec_buffer_string(&mut ret, &mut data_block, &mut sec_buffer_offset, self.flags, &self.workstation_name)?;
797        append_sec_buffer(&mut ret, &mut data_block, &mut sec_buffer_offset, &self.session_key);
798        ret.extend_from_slice(&self.flags.bits().to_le_bytes());
799        ret.extend_from_slice(&self.os_version.to_bytes());
800        ret.append(&mut data_block);
801        Ok(ret)
802    }
803}
804impl TryFrom<&[u8]> for AuthenticateMessage {
805    type Error = ParsingError;
806
807    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
808        // magic and message type have already been sliced away
809
810        if value.len() < 60 {
811            return Err(ParsingError::ItemMinLengthMismatch { expected_at_least: 60, obtained: value.len() });
812        }
813        let lm_response_secbuf = SecurityBuffer::try_from(&value[0..8]).unwrap();
814        let ntlm_response_secbuf = SecurityBuffer::try_from(&value[8..16]).unwrap();
815        let domain_name_secbuf = SecurityBuffer::try_from(&value[16..24]).unwrap();
816        let user_name_secbuf = SecurityBuffer::try_from(&value[24..32]).unwrap();
817        let workstation_name_secbuf = SecurityBuffer::try_from(&value[32..40]).unwrap();
818        let session_key_secbuf = SecurityBuffer::try_from(&value[40..48]).unwrap();
819        let flags_u32 = u32::from_le_bytes(value[48..52].try_into().unwrap());
820        let flags = Flags::from_bits(flags_u32).unwrap();
821        let os_version = if flags.contains(Flags::NEGOTIATE_VERSION) {
822            OsVersion::try_from(&value[52..60]).unwrap()
823        } else {
824            OsVersion::default()
825        };
826
827        // offsets in secbufs are in relation to the start of the message
828        // however, magic and message type have already been sliced away
829        // adjust offsets accordingly
830        let lm_response_bytes = lm_response_secbuf.apply_to_slice(&value, -(8 + 4))?;
831        let ntlm_response_bytes = ntlm_response_secbuf.apply_to_slice(&value, -(8 + 4))?;
832        let domain_name_bytes = domain_name_secbuf.apply_to_slice(&value, -(8 + 4))?;
833        let user_name_bytes = user_name_secbuf.apply_to_slice(&value, -(8 + 4))?;
834        let workstation_name_bytes = workstation_name_secbuf.apply_to_slice(&value, -(8 + 4))?;
835        let session_key_bytes = session_key_secbuf.apply_to_slice(&value, -(8 + 4))?;
836
837        let lm_response = Vec::from(lm_response_bytes);
838        let ntlm_response = Vec::from(ntlm_response_bytes);
839        let domain_name = ntlm_bytes_to_string(flags, domain_name_bytes)?;
840        let user_name = ntlm_bytes_to_string(flags, user_name_bytes)?;
841        let workstation_name = ntlm_bytes_to_string(flags, workstation_name_bytes)?;
842        let session_key = Vec::from(session_key_bytes);
843
844        Ok(Self {
845            lm_response,
846            ntlm_response,
847            domain_name,
848            user_name,
849            workstation_name,
850            session_key,
851            flags,
852            os_version,
853        })
854    }
855}
856
857impl SecurityBuffer {
858    /// Generates a security buffer for the given slice of bytes.
859    ///
860    /// The length and capacity are set to the length of the slice, while the offset is set to 0.
861    pub fn for_slice(slice: &[u8]) -> Self {
862        let len_u16: u16 = slice.len()
863            .try_into().expect("buffer too long for u16 length");
864        Self {
865            length: len_u16,
866            capacity: len_u16,
867            offset: 0,
868        }
869    }
870
871    /// Serializes the security buffer into bytes.
872    pub fn to_bytes(&self) -> Vec<u8> {
873        let mut ret = Vec::with_capacity(8);
874        ret.extend_from_slice(&self.length.to_le_bytes());
875        ret.extend_from_slice(&self.capacity.to_le_bytes());
876        ret.extend_from_slice(&self.offset.to_le_bytes());
877        ret
878    }
879
880    /// Applies the security buffer to a slice, extracting the data itself.
881    ///
882    /// The data is assumed to be located in `slice` beginning at `self.offset`. In case leading
883    /// fields have been removed from `slice`, the offset may be further adjusted through `adjust`.
884    pub fn apply_to_slice<'a>(&self, slice: &'a [u8], adjust: isize) -> Result<&'a [u8], ParsingError> {
885        if self.length == 0 {
886            // short-circuit
887            return Ok(&slice[0..0]);
888        }
889
890        let offset_isize: isize = self.offset.try_into()
891            .or(Err(ParsingError::OffsetTooLargeIsize))?;
892        let length_isize: isize = self.length.try_into()
893            .or(Err(ParsingError::LengthTooLargeIsize))?;
894
895        if offset_isize + adjust < 0 {
896            return Err(ParsingError::StartOutOfRange { start: offset_isize + adjust, length: slice.len() });
897        }
898        if offset_isize + length_isize + adjust < 0 {
899            return Err(ParsingError::EndOutOfRange { end: offset_isize + length_isize + adjust, length: slice.len() });
900        }
901
902        let start: usize = (offset_isize + adjust).try_into().unwrap();
903        let end: usize = (offset_isize + length_isize + adjust).try_into().unwrap();
904
905        if start >= slice.len() {
906            return Err(ParsingError::StartOutOfRange { start: offset_isize + adjust, length: slice.len() });
907        }
908        if end > slice.len() {
909            return Err(ParsingError::EndOutOfRange { end: offset_isize + length_isize + adjust, length: slice.len() });
910        }
911
912        Ok(&slice[start..end])
913    }
914}
915impl TryFrom<&[u8]> for SecurityBuffer {
916    type Error = ParsingError;
917
918    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
919        if value.len() != 8 {
920            return Err(ParsingError::ItemLengthMismatch { expected: 8, obtained: value.len() });
921        }
922
923        let length = u16::from_le_bytes(value[0..2].try_into().unwrap());
924        let capacity = u16::from_le_bytes(value[2..4].try_into().unwrap());
925        let offset = u32::from_le_bytes(value[4..8].try_into().unwrap());
926
927        Ok(Self {
928            length,
929            capacity,
930            offset,
931        })
932    }
933}
934
935impl TargetInfoEntry {
936    /// Serializes the target info entry into bytes.
937    pub fn to_bytes(&self) -> Vec<u8> {
938        let mut ret = Vec::new();
939
940        // always Unicode, even if flags claim OEM
941        let entry_type_u16: u16 = self.entry_type.into();
942        let bytes_len: u16 = self.data.len().try_into().expect("length of bytes does not fit into u16");
943
944        ret.extend_from_slice(&entry_type_u16.to_le_bytes());
945        ret.extend_from_slice(&bytes_len.to_le_bytes());
946        ret.extend_from_slice(&self.data);
947        ret
948    }
949
950    /// Attempts to deserialize a target info entry from the given byte slice. If successful,
951    /// returns the deserialized target info entry as well as any bytes remaining in the slice (that
952    /// are not part of the freshly deserialized target info entry).
953    pub fn try_from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), ParsingError> {
954        if bytes.len() < 4 {
955            return Err(ParsingError::ItemMinLengthMismatch { expected_at_least: 4, obtained: bytes.len() });
956        }
957
958        let entry_type_u16 = u16::from_le_bytes(bytes[0..2].try_into().unwrap());
959        let entry_type: TargetInfoType = entry_type_u16.into();
960        let length_u16 = u16::from_le_bytes(bytes[2..4].try_into().unwrap());
961        let length: usize = length_u16.into();
962
963        if length + 4 > bytes.len() {
964            return Err(ParsingError::ItemMinLengthMismatch { expected_at_least: length + 4, obtained: bytes.len() });
965        }
966        if length % 2 != 0 {
967            return Err(ParsingError::ItemLengthNotDivisible { expected_divisor: 2, obtained_length: bytes.len() });
968        }
969
970        let data = Vec::from(&bytes[4..4+length]);
971
972        let entry = Self {
973            entry_type,
974            data,
975        };
976        let rest = &bytes[4+length..];
977        Ok((entry, rest))
978    }
979
980    /// Attempts to convert the data within this target info entry into a string.
981    pub fn to_string(&self) -> Result<String, ParsingError> {
982        utf16_le_bytes_to_string(&self.data)
983    }
984
985    /// Creates a target info entry from an entry type and a string.
986    pub fn from_string(entry_type: TargetInfoType, string: &str) -> Self {
987        // always Unicode, even if flags claim OEM
988        let data: Vec<u8> = string.encode_utf16()
989            .flat_map(|b| b.to_le_bytes())
990            .collect();
991        Self {
992            entry_type,
993            data,
994        }
995    }
996}
997
998impl ChallengeResponse {
999    /// Converts this response to a challenge into a full-blown Authenticate NTLM message.
1000    pub fn to_message(&self, creds: &Credentials, workstation_name: &str, flags: Flags) -> Message {
1001        Message::Authenticate(AuthenticateMessage {
1002            lm_response: self.lm_response.clone(),
1003            ntlm_response: self.ntlm_response.clone(),
1004            domain_name: creds.domain.clone(),
1005            user_name: creds.username.clone(),
1006            workstation_name: workstation_name.to_owned(),
1007            session_key: self.session_key.clone(),
1008            flags,
1009            os_version: Default::default(),
1010        })
1011    }
1012}
1013
1014
1015// response calculation functions
1016
1017
1018/// Obtains the current NTLM timestamp.
1019pub fn get_ntlm_time() -> i64 {
1020    let windows_epoch = NaiveDate::from_ymd_opt(1601, 1, 1)
1021        .expect("1601-01-01 is not a valid date?!")
1022        .and_hms_opt(0, 0, 0).expect("1601-01-01T00:00:00 is not a valid date-time?!")
1023        .and_utc();
1024    let now = Utc::now();
1025    // the requested format is "tenths of a microsecond", so multiply by 10_000_000 and read seconds
1026    let delta = (now - windows_epoch) * 10_000_000;
1027    delta.num_seconds()
1028}
1029
1030
1031/// Performs the NTLMv1 DES encryption to calculate the response value to the challenge.
1032pub fn des_long(key: [u8; 16], data: [u8; 8]) -> [u8; 24] {
1033    let key0: [u8; 7] = key[0..7].try_into().unwrap();
1034    let key1: [u8; 7] = key[7..14].try_into().unwrap();
1035    let key2: [u8; 7] = [key[14], key[15], 0, 0, 0, 0, 0];
1036
1037    let des0 = Des::new_from_slice(&key0).unwrap();
1038    let des1 = Des::new_from_slice(&key1).unwrap();
1039    let des2 = Des::new_from_slice(&key2).unwrap();
1040
1041    let mut res0: GenericArray<u8, U8> = data.try_into().unwrap();
1042    let mut res1 = res0.clone();
1043    let mut res2 = res0.clone();
1044
1045    des0.encrypt_block(&mut res0);
1046    des1.encrypt_block(&mut res1);
1047    des2.encrypt_block(&mut res2);
1048
1049    let mut ret = [0u8; 24];
1050    let (slice0, ret2) = ret.split_at_mut(8);
1051    let (slice1, slice2) = ret2.split_at_mut(8);
1052    slice0.copy_from_slice(res0.as_slice());
1053    slice1.copy_from_slice(res1.as_slice());
1054    slice2.copy_from_slice(res2.as_slice());
1055
1056    ret
1057}
1058
1059
1060/// Derives the encryption key from a password according to the LMv1 scheme.
1061///
1062/// The LMv1 scheme consists of the following:
1063///
1064/// ```plain
1065///            ┌────────────┐  ┌───────────────┐  ┌─────────────┐
1066/// password ──┤ convert to ├──┤ encode using  ├──┤ truncate or ├──┐
1067///            │ uppercase  │  │ ANSI codepage │  │ pad to 14 B │  │
1068///            └────────────┘  └───────────────┘  └─────────────┘  │
1069///           ┌────────────────────────────────────────────────────┘
1070///           │┌───────┐
1071///           └┤ split │           "KGS!@#$%"
1072///            └─┬───┬─┘               │ input
1073///         0..7 │   │ 7..14    key ┌──┴──┐ output
1074///              │   └──────────────┤ DES ├────────────┐
1075///              │                  └─────┘            │ 0..8
1076///              │                                  ┌──┴───┐
1077///              │                 "KGS!@#$%"       │ join ├──── key
1078///              │                     │ input      └──┬───┘
1079///              │              key ┌──┴──┐ output     │ 8..16
1080///              └──────────────────┤ DES ├────────────┘
1081///                                 └─────┘
1082/// ```
1083pub fn lm_v1_password_func(password: &str) -> [u8; 16] {
1084    let des_plaintext_fixed: GenericArray<u8, U8> = GenericArray::from(*b"KGS!@#$%");
1085
1086    let uppercase_password = password.to_uppercase();
1087    let mut password_bytes = match rust_string_to_ansi(&uppercase_password) {
1088        Some(bs) => bs,
1089        None => return [0; 16],
1090    };
1091    while password_bytes.len() < 14 {
1092        password_bytes.push(0x00);
1093    }
1094
1095    let mut output = [0; 16];
1096    let (half0, half1) = output.split_at_mut(8);
1097
1098    {
1099        {
1100            let des_state = Des::new_from_slice(&password_bytes[0..7]).unwrap();
1101            let mut buf = des_plaintext_fixed.clone();
1102            des_state.encrypt_block(&mut buf);
1103            half0.copy_from_slice(buf.as_slice());
1104        }
1105        {
1106            let des_state = Des::new_from_slice(&password_bytes[7..14]).unwrap();
1107            let mut buf = des_plaintext_fixed.clone();
1108            des_state.encrypt_block(&mut buf);
1109            half1.copy_from_slice(buf.as_slice());
1110        }
1111    }
1112
1113    output
1114}
1115
1116
1117/// Derives the encryption key from a password according to the NTLMv1 scheme.
1118///
1119/// The NTLMv1 scheme encodes the password as UTF-16 in little-endian byte order (without the Byte
1120/// Order Mark) and hashes it using MD4.
1121pub fn ntlm_v1_password_func(password: &str) -> [u8; 16] {
1122    let password_bytes: Vec<u8> = password.encode_utf16()
1123        .flat_map(|p| p.to_le_bytes())
1124        .collect();
1125    let mut md4_state = <Md4 as Digest>::new();
1126    md4_state.update(&password_bytes);
1127    md4_state.finalize().as_slice().try_into().unwrap()
1128}
1129
1130/// Derives the encryption key from a password according to the NTLMv2 scheme.
1131///
1132/// The NTLMv2 scheme is a HMAC-MD5 scheme whose key is the encryption key derived from the password
1133/// using the NTLMv1 scheme and whose plaintext is a concatenation of uppercase username and
1134/// unchanged-case domain, each encoded as UTF-16 in little-endian byte order without the Byte Order
1135/// Mark.
1136pub fn ntlm_v2_password_func(creds: &Credentials) -> [u8; 16] {
1137    // the HMAC key func is the same as the NTLMv1 password func
1138    let hmac_key = ntlm_v1_password_func(&creds.password);
1139    let mut hmac_md5: Hmac<Md5> = <Hmac<Md5> as Mac>::new_from_slice(&hmac_key).unwrap();
1140
1141    let upper_user_bytes: Vec<u8> = creds.username
1142        .to_uppercase()
1143        .encode_utf16()
1144        .flat_map(|p| p.to_le_bytes())
1145        .collect();
1146    hmac_md5.update(&upper_user_bytes);
1147    let dom_bytes: Vec<u8> = creds.domain
1148        .encode_utf16()
1149        .flat_map(|p| p.to_le_bytes())
1150        .collect();
1151    hmac_md5.update(&dom_bytes);
1152
1153    let mut ret = [0; 16];
1154    ret.copy_from_slice(hmac_md5.finalize().into_bytes().as_slice());
1155    ret
1156}
1157
1158/// Calculates an NTLMv1 response to the given server challenge.
1159///
1160/// An LMv1 response is also included.
1161pub fn respond_challenge_ntlm_v1(server_challenge: [u8; 8], creds: &Credentials) -> ChallengeResponse {
1162    let ntlm_key = ntlm_v1_password_func(&creds.password);
1163    let ntlm_response = Vec::from(des_long(ntlm_key, server_challenge));
1164
1165    let lm_key = lm_v1_password_func(&creds.password);
1166    let lm_response = Vec::from(des_long(lm_key, server_challenge));
1167
1168    let session_key = {
1169        let mut md4 = <Md4 as Digest>::new();
1170        md4.update(&ntlm_key);
1171        Vec::from(md4.finalize().as_slice())
1172    };
1173
1174    ChallengeResponse {
1175        lm_response,
1176        ntlm_response,
1177        session_key,
1178    }
1179}
1180
1181/// Calculates an NTLMv1 response to the given server challenge.
1182///
1183/// No LM response is included; instead, the NTLMv1 response is copied.
1184pub fn respond_challenge_ntlm_v1_no_lm(server_challenge: [u8; 8], creds: &Credentials) -> ChallengeResponse {
1185    let ntlm_key = ntlm_v1_password_func(&creds.password);
1186    let ntlm_response = Vec::from(des_long(ntlm_key, server_challenge));
1187
1188    let lm_response = ntlm_response.clone();
1189
1190    let session_key = {
1191        let mut md4 = <Md4 as Digest>::new();
1192        md4.update(&ntlm_key);
1193        Vec::from(md4.finalize().as_slice())
1194    };
1195
1196    ChallengeResponse {
1197        lm_response,
1198        ntlm_response,
1199        session_key,
1200    }
1201}
1202
1203/// Calculates an extended NTLMv1 response to the given server challenge.
1204///
1205/// The NTLM response is in the extended format; the LM response field contains the randomly
1206/// generated client challenge which has also influenced the calculation of the NTLM response.
1207pub fn respond_challenge_ntlm_v1_extended(server_challenge: [u8; 8], creds: &Credentials) -> ChallengeResponse {
1208    let mut client_challenge: [u8; 8] = [0; 8];
1209    OsRng.fill(&mut client_challenge);
1210
1211    let ntlm_key = ntlm_v1_password_func(&creds.password);
1212
1213    let desl_plaintext: [u8; 8] = {
1214        let mut md5 = <Md5 as Digest>::new();
1215        md5.update(server_challenge);
1216        md5.update(client_challenge);
1217        let digest = md5.finalize();
1218
1219        let mut dk = [0u8; 8];
1220        dk.copy_from_slice(&digest.as_slice()[0..8]);
1221        dk
1222    };
1223
1224    let ntlm_response = Vec::from(des_long(ntlm_key, desl_plaintext));
1225    let mut lm_response = Vec::with_capacity(24);
1226    lm_response.extend_from_slice(&client_challenge);
1227    while lm_response.len() < 24 {
1228        lm_response.push(0);
1229    }
1230
1231    let session_key = {
1232        let mut md4 = <Md4 as Digest>::new();
1233        md4.update(&ntlm_key);
1234        Vec::from(md4.finalize().as_slice())
1235    };
1236
1237    ChallengeResponse {
1238        lm_response,
1239        ntlm_response,
1240        session_key,
1241    }
1242}
1243
1244/// Calculates an NTLMv2 response to the given server challenge, including target info and time
1245/// value to protect against replay attacks.
1246pub fn respond_challenge_ntlm_v2(server_challenge: [u8; 8], target_info: &[u8], time: i64, creds: &Credentials) -> ChallengeResponse {
1247    let mut client_challenge: [u8; 8] = [0; 8];
1248    OsRng.fill(&mut client_challenge);
1249
1250    let mut temp = Vec::new();
1251    temp.push(0x01); // Responserversion
1252    temp.push(0x01); // HiResponserversion
1253    for _ in 0..6 { temp.push(0x00); }
1254    temp.extend_from_slice(&time.to_le_bytes());
1255    temp.extend_from_slice(&client_challenge);
1256    for _ in 0..4 { temp.push(0x00); }
1257    temp.extend_from_slice(&target_info);
1258    for _ in 0..4 { temp.push(0x00); }
1259
1260    let ntlm_key = ntlm_v2_password_func(&creds);
1261
1262    let nt_proof_string = {
1263        let mut hmac_md5: Hmac<Md5> = <Hmac<Md5> as Mac>::new_from_slice(&ntlm_key).unwrap();
1264        hmac_md5.update(&server_challenge);
1265        hmac_md5.update(&temp);
1266
1267        let mut ps = [0u8; 16];
1268        ps.copy_from_slice(hmac_md5.finalize().into_bytes().as_slice());
1269        ps
1270    };
1271
1272    let mut ntlm_response = Vec::with_capacity(16 + temp.len());
1273    ntlm_response.extend_from_slice(&nt_proof_string);
1274    ntlm_response.extend_from_slice(&temp);
1275
1276    let mut lm_response = Vec::with_capacity(16 + 8);
1277    {
1278        let mut hmac_md5: Hmac<Md5> = <Hmac<Md5> as Mac>::new_from_slice(&ntlm_key).unwrap();
1279        hmac_md5.update(&server_challenge);
1280        hmac_md5.update(&client_challenge);
1281        lm_response.extend_from_slice(hmac_md5.finalize().into_bytes().as_slice());
1282    }
1283    lm_response.extend_from_slice(&client_challenge);
1284
1285    let session_key = {
1286        let mut hmac_md5: Hmac<Md5> = <Hmac<Md5> as Mac>::new_from_slice(&ntlm_key).unwrap();
1287        hmac_md5.update(&nt_proof_string);
1288        Vec::from(hmac_md5.finalize().into_bytes().as_slice())
1289    };
1290
1291    ChallengeResponse {
1292        lm_response,
1293        ntlm_response,
1294        session_key,
1295    }
1296}