picky_krb/negoex/
data_types.rs

1use std::io::{self, Read, Write};
2
3use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
4use uuid::Uuid;
5
6use super::{CHECKSUM_SCHEME_RFC3961, NEGOEXTS_MESSAGE_SIGNATURE, NegoexDataType};
7
8const GUID_SIZE: usize = 16;
9pub(crate) const CHECKSUM_HEADER_LEN: u32 = 4 /* header_len */ + 4 /* checksum_scheme */ + 4 /* type */ + 8 /* checksum vector header */;
10
11pub type Guid = Uuid;
12
13impl NegoexDataType for Guid {
14    type Error = io::Error;
15
16    fn size(&self) -> usize {
17        GUID_SIZE
18    }
19
20    fn decode(mut from: impl Read, _message: &[u8]) -> Result<Self, Self::Error> {
21        let mut id_bytes = [0; GUID_SIZE];
22        from.read_exact(&mut id_bytes)?;
23
24        Ok(Self::from_bytes_le(id_bytes))
25    }
26
27    fn encode_with_payload(&self, _offset: usize, mut to: impl Write, _data: impl Write) -> Result<usize, Self::Error> {
28        to.write_all(&self.to_bytes_le())?;
29
30        Ok(0)
31    }
32
33    fn encode(&self, to: impl Write) -> Result<(), Self::Error> {
34        self.encode_with_payload(0, to, &mut [] as &mut [u8])?;
35
36        Ok(())
37    }
38}
39
40/// [2.2.2 GUID typedefs](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
41/// ```not_rust
42/// typedef GUID CONVERSATION_ID;
43/// ```
44pub type ConversationId = Guid;
45
46/// [2.2.2 GUID typedefs](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
47/// ```not_rust
48/// typedef GUID AUTH_SCHEME;
49/// ```
50pub type AuthScheme = Guid;
51
52//= message type are always have a size of 4 bytes =//
53const MESSAGE_TYPE_SIZE: usize = 4;
54
55/// [2.2.6.1 MESSAGE_TYPE](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
56/// ```not_rust
57/// enum
58/// {
59///     MESSAGE_TYPE_INITIATOR_NEGO = 0,
60///     MESSAGE_TYPE_ACCEPTOR_NEGO,
61///     MESSAGE_TYPE_INITIATOR_META_DATA,
62///     MESSAGE_TYPE_ACCEPTOR_META_DATA,
63///     MESSAGE_TYPE_CHALLENGE,
64///     MESSAGE_TYPE_AP_REQUEST,
65///     MESSAGE_TYPE_VERIFY,
66///     MESSAGE_TYPE_ALERT
67/// } MESSAGE_TYPE;
68/// ```
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub enum MessageType {
71    InitiatorNego,
72    AcceptorNego,
73    InitiatorMetaData,
74    AcceptorMetaData,
75    Challenge,
76    ApRequest,
77    Verify,
78    Alert,
79}
80
81impl NegoexDataType for MessageType {
82    type Error = io::Error;
83
84    fn size(&self) -> usize {
85        MESSAGE_TYPE_SIZE
86    }
87
88    fn decode(mut from: impl Read, _message: &[u8]) -> Result<Self, Self::Error> {
89        MessageType::try_from(from.read_u32::<LittleEndian>()?)
90    }
91
92    fn encode_with_payload(&self, _offset: usize, mut to: impl Write, _data: impl Write) -> Result<usize, Self::Error> {
93        to.write_u32::<LittleEndian>(self.into())?;
94
95        Ok(0)
96    }
97
98    fn encode(&self, to: impl Write) -> Result<(), Self::Error> {
99        self.encode_with_payload(0, to, &mut [] as &mut [u8])?;
100
101        Ok(())
102    }
103}
104
105impl TryFrom<u32> for MessageType {
106    type Error = io::Error;
107
108    fn try_from(type_raw: u32) -> Result<Self, Self::Error> {
109        match type_raw {
110            0 => Ok(MessageType::InitiatorNego),
111            1 => Ok(MessageType::AcceptorNego),
112            2 => Ok(MessageType::InitiatorMetaData),
113            3 => Ok(MessageType::AcceptorMetaData),
114            4 => Ok(MessageType::Challenge),
115            5 => Ok(MessageType::ApRequest),
116            6 => Ok(MessageType::Verify),
117            7 => Ok(MessageType::Alert),
118            _ => Err(io::Error::new(io::ErrorKind::InvalidData, "invalid MessageType")),
119        }
120    }
121}
122
123impl From<&MessageType> for u32 {
124    fn from(message_type: &MessageType) -> Self {
125        match message_type {
126            MessageType::InitiatorNego => 0,
127            MessageType::AcceptorNego => 1,
128            MessageType::InitiatorMetaData => 2,
129            MessageType::AcceptorMetaData => 3,
130            MessageType::Challenge => 4,
131            MessageType::ApRequest => 5,
132            MessageType::Verify => 6,
133            MessageType::Alert => 7,
134        }
135    }
136}
137
138/// [2.2.6.2 MESSAGE_HEADER](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
139/// ```not_rust
140/// struct
141/// {
142///     ULONG64 Signature;
143///     MESSAGE_TYPE MessageType;
144///     ULONG SequenceNum;
145///     ULONG cbHeaderLength;
146///     ULONG cbMessageLength;
147///     CONVERSATION_ID ConversationId;
148/// } MESSAGE_HEADER;
149/// ```
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub struct MessageHeader {
152    pub signature: u64,
153    pub message_type: MessageType,
154    pub sequence_num: u32,
155    pub header_len: u32,
156    pub message_len: u32,
157    pub conversation_id: ConversationId,
158}
159
160impl NegoexDataType for MessageHeader {
161    type Error = io::Error;
162
163    fn size(&self) -> usize {
164        8 /* signature */ +
165        self.message_type.size() +
166        4 /* sequence_num */ +
167        4 /* header_len */ +
168        4 /* message_len */ +
169        self.conversation_id.size()
170    }
171
172    fn decode(mut from: impl Read, message: &[u8]) -> Result<Self, Self::Error> {
173        let signature = from.read_u64::<LittleEndian>()?;
174
175        if signature != NEGOEXTS_MESSAGE_SIGNATURE {
176            return Err(io::Error::new(
177                io::ErrorKind::InvalidData,
178                format!(
179                    "invalid message signature: {:x?}. expected: {:x?}",
180                    signature, NEGOEXTS_MESSAGE_SIGNATURE
181                ),
182            ));
183        }
184
185        let message_type = MessageType::decode(&mut from, message)?;
186
187        let sequence_num = from.read_u32::<LittleEndian>()?;
188
189        let header_len = from.read_u32::<LittleEndian>()?;
190
191        let message_len = from.read_u32::<LittleEndian>()?;
192
193        let conversation_id = ConversationId::decode(&mut from, message)?;
194
195        Ok(Self {
196            signature,
197            message_type,
198            sequence_num,
199            header_len,
200            message_len,
201            conversation_id,
202        })
203    }
204
205    fn encode_with_payload(
206        &self,
207        offset: usize,
208        mut to: impl Write,
209        mut data: impl Write,
210    ) -> Result<usize, Self::Error> {
211        to.write_u64::<LittleEndian>(self.signature)?;
212
213        let message_type_offset = self.message_type.encode_with_payload(offset, &mut to, &mut data)?;
214
215        to.write_u32::<LittleEndian>(self.sequence_num)?;
216
217        to.write_u32::<LittleEndian>(self.header_len)?;
218
219        to.write_u32::<LittleEndian>(self.message_len)?;
220
221        let conversation_id_offset = self.conversation_id.encode_with_payload(offset, &mut to, &mut data)?;
222
223        Ok(message_type_offset + conversation_id_offset)
224    }
225
226    fn encode(&self, to: impl Write) -> Result<(), Self::Error> {
227        self.encode_with_payload(0, to, &mut [] as &mut [u8])?;
228
229        Ok(())
230    }
231}
232
233/// [2.2.5.1.4 EXTENSION](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
234/// ```not_rust
235/// struct
236/// {
237///     ULONG ExtensionType;
238///     BYTE_VECTOR ExtensionValue;
239/// } EXTENSION;
240/// ```
241#[derive(Debug, Clone, PartialEq, Eq)]
242pub struct Extension {
243    pub extension_type: u32,
244    pub extension_value: ByteVector,
245}
246
247impl NegoexDataType for Extension {
248    type Error = io::Error;
249
250    fn size(&self) -> usize {
251        4 /* extension_type */ + self.extension_value.len()
252    }
253
254    fn decode(mut from: impl Read, message: &[u8]) -> Result<Self, Self::Error> {
255        let extension_type = from.read_u32::<LittleEndian>()?;
256
257        let extension_value = ByteVector::decode(&mut from, message)?;
258
259        Ok(Self {
260            extension_type,
261            extension_value,
262        })
263    }
264
265    fn encode_with_payload(
266        &self,
267        offset: usize,
268        mut to: impl Write,
269        mut data: impl Write,
270    ) -> Result<usize, Self::Error> {
271        to.write_u32::<LittleEndian>(self.extension_type)?;
272
273        self.extension_value.encode_with_payload(offset, &mut to, &mut data)
274    }
275
276    fn encode(&self, mut to: impl Write) -> Result<(), Self::Error> {
277        let offset = 12;
278
279        let mut header = Vec::new();
280        let mut data = Vec::new();
281
282        self.encode_with_payload(offset, &mut header, &mut data)?;
283
284        to.write_all(&header)?;
285        to.write_all(&data)?;
286
287        Ok(())
288    }
289}
290
291/// [2.2.5.2.3 BYTE_VECTOR](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
292/// ```not_rust
293/// struct
294/// {
295///     ULONG ByteArrayOffset;
296///     ULONG ByteArrayLength;
297/// } BYTE_VECTOR;
298/// ```
299pub type ByteVector = Vec<u8>;
300
301/// [2.2.5.2.2 AUTH_SCHEME_VECTOR](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
302/// ```not_rust
303/// struct
304/// {
305///     ULONG AuthSchemeArrayOffset;
306///     USHORT AuthSchemeCount;
307/// } AUTH_SCHEME_VECTOR;
308/// ```
309pub type AuthSchemeVector = Vec<AuthScheme>;
310
311/// [2.2.5.2.4 EXTENSION_VECTOR](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
312/// ```not_rust
313/// struct
314/// {
315///     ULONG ExtensionArrayOffset;
316///     USHORT ExtensionCount;
317/// } EXTENSION_VECTOR;
318/// ```
319pub type ExtensionVector = Vec<Extension>;
320
321/// [2.2.5.1.3 CHECKSUM](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NEGOEX/%5bMS-NEGOEX%5d.pdf)
322/// ```not_rust
323/// struct
324/// {
325///     ULONG cbHeaderLength;
326///     ULONG ChecksumScheme;
327///     ULONG ChecksumType;
328///     BYTE_VECTOR ChecksumValue;
329/// } CHECKSUM;
330/// ```
331#[derive(Debug, Clone, PartialEq, Eq)]
332pub struct Checksum {
333    pub header_len: u32,
334    pub checksum_scheme: u32,
335    pub checksum_type: u32,
336    pub checksum_value: Vec<u8>,
337}
338
339impl NegoexDataType for Checksum {
340    type Error = io::Error;
341
342    fn size(&self) -> usize {
343        4 /* header_len */ +
344        4 /* checksum_scheme */ +
345        4 /* checksum type */ +
346        4 /* padding of 4 bytes */ +
347        self.checksum_value.size()
348    }
349
350    fn decode(mut from: impl Read, message: &[u8]) -> Result<Self, Self::Error> {
351        let header_len = from.read_u32::<LittleEndian>()?;
352
353        let checksum_scheme = from.read_u32::<LittleEndian>()?;
354
355        if checksum_scheme != CHECKSUM_SCHEME_RFC3961 {
356            return Err(io::Error::new(
357                io::ErrorKind::InvalidInput,
358                format!(
359                    "invalid checksum scheme: {}. Expected: {}",
360                    checksum_scheme, CHECKSUM_SCHEME_RFC3961
361                ),
362            ));
363        }
364
365        let checksum_type = from.read_u32::<LittleEndian>()?;
366
367        let checksum_value = Vec::decode(&mut from, message)?;
368
369        Ok(Self {
370            header_len,
371            checksum_scheme,
372            checksum_type,
373            checksum_value,
374        })
375    }
376
377    fn encode_with_payload(
378        &self,
379        offset: usize,
380        mut to: impl Write,
381        mut data: impl Write,
382    ) -> Result<usize, Self::Error> {
383        to.write_u32::<LittleEndian>(self.header_len)?;
384
385        to.write_u32::<LittleEndian>(self.checksum_scheme)?;
386
387        to.write_u32::<LittleEndian>(self.checksum_type)?;
388
389        self.checksum_value.encode_with_payload(offset, &mut to, &mut data)
390    }
391
392    fn encode(&self, mut to: impl Write) -> Result<(), Self::Error> {
393        let offset = self.header_len as usize;
394
395        let mut header = Vec::new();
396        let mut data = Vec::new();
397
398        self.encode_with_payload(offset, &mut header, &mut data)?;
399
400        to.write_all(&header)?;
401        to.write_all(&data)?;
402
403        Ok(())
404    }
405}
406
407#[cfg(test)]
408mod tests {
409    use std::str::FromStr;
410
411    use uuid::Uuid;
412
413    use crate::constants::cksum_types::HMAC_SHA1_96_AES256;
414    use crate::negoex::NegoexDataType;
415    use crate::negoex::data_types::Guid;
416
417    use super::{CHECKSUM_SCHEME_RFC3961, Checksum, Extension, MessageHeader, MessageType, NEGOEXTS_MESSAGE_SIGNATURE};
418
419    #[test]
420    fn guid_encode() {
421        let guid = Uuid::from_str("0d53335c-f9ea-4d0d-b2ec-4ae3786ec308").unwrap();
422
423        let mut encoded = Vec::new();
424        guid.encode(&mut encoded).unwrap();
425
426        assert_eq!(
427            &[92, 51, 83, 13, 234, 249, 13, 77, 178, 236, 74, 227, 120, 110, 195, 8],
428            encoded.as_slice()
429        );
430    }
431
432    #[test]
433    fn guid_decode() {
434        let encoded_guid = [90, 7, 41, 59, 145, 243, 51, 175, 161, 180, 162, 18, 36, 157, 124, 180];
435
436        let guid = Guid::decode(&encoded_guid as &[u8], &encoded_guid).unwrap();
437
438        assert_eq!(Uuid::from_str("3b29075a-f391-af33-a1b4-a212249d7cb4").unwrap(), guid);
439    }
440
441    #[test]
442    fn message_type_decode() {
443        let encoded = [1, 0, 0, 0];
444
445        let message_type = MessageType::decode(&encoded as &[u8], &encoded).unwrap();
446
447        assert_eq!(MessageType::AcceptorNego, message_type);
448    }
449
450    #[test]
451    fn message_type_encode() {
452        let message_type = MessageType::ApRequest;
453
454        let mut encoded = Vec::new();
455        message_type.encode(&mut encoded).unwrap();
456
457        assert_eq!(&[5, 0, 0, 0], encoded.as_slice());
458    }
459
460    #[test]
461    fn message_header_encode() {
462        let message_header = MessageHeader {
463            signature: NEGOEXTS_MESSAGE_SIGNATURE,
464            message_type: MessageType::AcceptorNego,
465            sequence_num: 2,
466            header_len: 96,
467            message_len: 112,
468            conversation_id: Guid::from_str("3b29075a-f391-af33-a1b4-a212249d7cb4").unwrap(),
469        };
470
471        let mut encoded = Vec::new();
472        message_header.encode(&mut encoded).unwrap();
473
474        assert_eq!(
475            &[
476                78, 69, 71, 79, 69, 88, 84, 83, 1, 0, 0, 0, 2, 0, 0, 0, 96, 0, 0, 0, 112, 0, 0, 0, 90, 7, 41, 59, 145,
477                243, 51, 175, 161, 180, 162, 18, 36, 157, 124, 180
478            ],
479            encoded.as_slice(),
480        );
481    }
482
483    #[test]
484    fn message_header_decode() {
485        let encoded = [
486            78, 69, 71, 79, 69, 88, 84, 83, 1, 0, 0, 0, 2, 0, 0, 0, 96, 0, 0, 0, 112, 0, 0, 0, 90, 7, 41, 59, 145, 243,
487            51, 175, 161, 180, 162, 18, 36, 157, 124, 180,
488        ];
489
490        let message_header = MessageHeader::decode(&encoded as &[u8], &encoded).unwrap();
491
492        assert_eq!(
493            MessageHeader {
494                signature: NEGOEXTS_MESSAGE_SIGNATURE,
495                message_type: MessageType::AcceptorNego,
496                sequence_num: 2,
497                header_len: 96,
498                message_len: 112,
499                conversation_id: Guid::from_str("3b29075a-f391-af33-a1b4-a212249d7cb4").unwrap(),
500            },
501            message_header,
502        );
503    }
504
505    #[test]
506    fn extension_encode() {
507        let extension = Extension {
508            extension_type: 3,
509            extension_value: vec![1, 2, 3, 4, 5, 6],
510        };
511
512        let mut encoded = Vec::new();
513        extension.encode(&mut encoded).unwrap();
514
515        assert_eq!(
516            &[3, 0, 0, 0, 12, 0, 0, 0, 6, 0, 0, 0, 1, 2, 3, 4, 5, 6],
517            encoded.as_slice()
518        );
519    }
520
521    #[test]
522    fn extension_decode() {
523        let encoded = [3, 0, 0, 0, 12, 0, 0, 0, 6, 0, 0, 0, 1, 2, 3, 4, 5, 6];
524
525        let extension = Extension::decode(&encoded as &[u8], &encoded).unwrap();
526
527        assert_eq!(
528            Extension {
529                extension_type: 3,
530                extension_value: vec![1, 2, 3, 4, 5, 6],
531            },
532            extension
533        );
534    }
535
536    #[test]
537    fn checksum_decode() {
538        // NEGOEX VERIFY message that contains the checksum
539        let negoex_verify = [
540            78, 69, 71, 79, 69, 88, 84, 83, 6, 0, 0, 0, 7, 0, 0, 0, 80, 0, 0, 0, 92, 0, 0, 0, 90, 7, 41, 59, 145, 243,
541            51, 175, 161, 180, 162, 18, 36, 157, 124, 180, 92, 51, 83, 13, 234, 249, 13, 77, 178, 236, 74, 227, 120,
542            110, 195, 8, 20, 0, 0, 0, 1, 0, 0, 0, 16, 0, 0, 0, 80, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 228, 167, 112,
543            148, 23, 131, 204, 12, 13, 36, 58, 87,
544        ];
545
546        // 56 - start of the Checksum struct
547        let checksum = Checksum::decode(&negoex_verify[56..], &negoex_verify).unwrap();
548
549        assert_eq!(
550            Checksum {
551                header_len: 20,
552                checksum_scheme: CHECKSUM_SCHEME_RFC3961,
553                checksum_type: HMAC_SHA1_96_AES256 as u32,
554                checksum_value: vec![228, 167, 112, 148, 23, 131, 204, 12, 13, 36, 58, 87],
555            },
556            checksum
557        );
558    }
559
560    #[test]
561    fn checksum_encode() {
562        let checksum = Checksum {
563            header_len: 20,
564            checksum_scheme: CHECKSUM_SCHEME_RFC3961,
565            checksum_type: HMAC_SHA1_96_AES256 as u32,
566            checksum_value: vec![228, 167, 112, 148, 23, 131, 204, 12, 13, 36, 58, 87],
567        };
568
569        let mut encoded = Vec::new();
570        checksum.encode(&mut encoded).unwrap();
571
572        assert_eq!(
573            &[
574                20, 0, 0, 0, 1, 0, 0, 0, 16, 0, 0, 0, 20, 0, 0, 0, 12, 0, 0, 0, 228, 167, 112, 148, 23, 131, 204, 12,
575                13, 36, 58, 87
576            ],
577            encoded.as_slice(),
578        )
579    }
580}