use std::io::{self, Read, Write};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use uuid::Uuid;
use super::{CHECKSUM_SCHEME_RFC3961, NEGOEXTS_MESSAGE_SIGNATURE, NegoexDataType};
const GUID_SIZE: usize = 16;
pub(crate) const CHECKSUM_HEADER_LEN: u32 = 4 + 4 + 4 + 8 ;
pub type Guid = Uuid;
impl NegoexDataType for Guid {
type Error = io::Error;
fn size(&self) -> usize {
GUID_SIZE
}
fn decode(mut from: impl Read, _message: &[u8]) -> Result<Self, Self::Error> {
let mut id_bytes = [0; GUID_SIZE];
from.read_exact(&mut id_bytes)?;
Ok(Self::from_bytes_le(id_bytes))
}
fn encode_with_payload(&self, _offset: usize, mut to: impl Write, _data: impl Write) -> Result<usize, Self::Error> {
to.write_all(&self.to_bytes_le())?;
Ok(0)
}
fn encode(&self, to: impl Write) -> Result<(), Self::Error> {
self.encode_with_payload(0, to, &mut [] as &mut [u8])?;
Ok(())
}
}
pub type ConversationId = Guid;
pub type AuthScheme = Guid;
const MESSAGE_TYPE_SIZE: usize = 4;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MessageType {
InitiatorNego,
AcceptorNego,
InitiatorMetaData,
AcceptorMetaData,
Challenge,
ApRequest,
Verify,
Alert,
}
impl NegoexDataType for MessageType {
type Error = io::Error;
fn size(&self) -> usize {
MESSAGE_TYPE_SIZE
}
fn decode(mut from: impl Read, _message: &[u8]) -> Result<Self, Self::Error> {
MessageType::try_from(from.read_u32::<LittleEndian>()?)
}
fn encode_with_payload(&self, _offset: usize, mut to: impl Write, _data: impl Write) -> Result<usize, Self::Error> {
to.write_u32::<LittleEndian>(self.into())?;
Ok(0)
}
fn encode(&self, to: impl Write) -> Result<(), Self::Error> {
self.encode_with_payload(0, to, &mut [] as &mut [u8])?;
Ok(())
}
}
impl TryFrom<u32> for MessageType {
type Error = io::Error;
fn try_from(type_raw: u32) -> Result<Self, Self::Error> {
match type_raw {
0 => Ok(MessageType::InitiatorNego),
1 => Ok(MessageType::AcceptorNego),
2 => Ok(MessageType::InitiatorMetaData),
3 => Ok(MessageType::AcceptorMetaData),
4 => Ok(MessageType::Challenge),
5 => Ok(MessageType::ApRequest),
6 => Ok(MessageType::Verify),
7 => Ok(MessageType::Alert),
_ => Err(io::Error::new(io::ErrorKind::InvalidData, "invalid MessageType")),
}
}
}
impl From<&MessageType> for u32 {
fn from(message_type: &MessageType) -> Self {
match message_type {
MessageType::InitiatorNego => 0,
MessageType::AcceptorNego => 1,
MessageType::InitiatorMetaData => 2,
MessageType::AcceptorMetaData => 3,
MessageType::Challenge => 4,
MessageType::ApRequest => 5,
MessageType::Verify => 6,
MessageType::Alert => 7,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MessageHeader {
pub signature: u64,
pub message_type: MessageType,
pub sequence_num: u32,
pub header_len: u32,
pub message_len: u32,
pub conversation_id: ConversationId,
}
impl NegoexDataType for MessageHeader {
type Error = io::Error;
fn size(&self) -> usize {
8 +
self.message_type.size() +
4 +
4 +
4 +
self.conversation_id.size()
}
fn decode(mut from: impl Read, message: &[u8]) -> Result<Self, Self::Error> {
let signature = from.read_u64::<LittleEndian>()?;
if signature != NEGOEXTS_MESSAGE_SIGNATURE {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("invalid message signature: {signature:x?}. expected: {NEGOEXTS_MESSAGE_SIGNATURE:x?}"),
));
}
let message_type = MessageType::decode(&mut from, message)?;
let sequence_num = from.read_u32::<LittleEndian>()?;
let header_len = from.read_u32::<LittleEndian>()?;
let message_len = from.read_u32::<LittleEndian>()?;
let conversation_id = ConversationId::decode(&mut from, message)?;
Ok(Self {
signature,
message_type,
sequence_num,
header_len,
message_len,
conversation_id,
})
}
fn encode_with_payload(
&self,
offset: usize,
mut to: impl Write,
mut data: impl Write,
) -> Result<usize, Self::Error> {
to.write_u64::<LittleEndian>(self.signature)?;
let message_type_offset = self.message_type.encode_with_payload(offset, &mut to, &mut data)?;
to.write_u32::<LittleEndian>(self.sequence_num)?;
to.write_u32::<LittleEndian>(self.header_len)?;
to.write_u32::<LittleEndian>(self.message_len)?;
let conversation_id_offset = self.conversation_id.encode_with_payload(offset, &mut to, &mut data)?;
Ok(message_type_offset + conversation_id_offset)
}
fn encode(&self, to: impl Write) -> Result<(), Self::Error> {
self.encode_with_payload(0, to, &mut [] as &mut [u8])?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Extension {
pub extension_type: u32,
pub extension_value: ByteVector,
}
impl NegoexDataType for Extension {
type Error = io::Error;
fn size(&self) -> usize {
4 + self.extension_value.len()
}
fn decode(mut from: impl Read, message: &[u8]) -> Result<Self, Self::Error> {
let extension_type = from.read_u32::<LittleEndian>()?;
let extension_value = ByteVector::decode(&mut from, message)?;
Ok(Self {
extension_type,
extension_value,
})
}
fn encode_with_payload(
&self,
offset: usize,
mut to: impl Write,
mut data: impl Write,
) -> Result<usize, Self::Error> {
to.write_u32::<LittleEndian>(self.extension_type)?;
self.extension_value.encode_with_payload(offset, &mut to, &mut data)
}
fn encode(&self, mut to: impl Write) -> Result<(), Self::Error> {
let offset = 12;
let mut header = Vec::new();
let mut data = Vec::new();
self.encode_with_payload(offset, &mut header, &mut data)?;
to.write_all(&header)?;
to.write_all(&data)?;
Ok(())
}
}
pub type ByteVector = Vec<u8>;
pub type AuthSchemeVector = Vec<AuthScheme>;
pub type ExtensionVector = Vec<Extension>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Checksum {
pub header_len: u32,
pub checksum_scheme: u32,
pub checksum_type: u32,
pub checksum_value: Vec<u8>,
}
impl NegoexDataType for Checksum {
type Error = io::Error;
fn size(&self) -> usize {
4 +
4 +
4 +
4 +
self.checksum_value.size()
}
fn decode(mut from: impl Read, message: &[u8]) -> Result<Self, Self::Error> {
let header_len = from.read_u32::<LittleEndian>()?;
let checksum_scheme = from.read_u32::<LittleEndian>()?;
if checksum_scheme != CHECKSUM_SCHEME_RFC3961 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("invalid checksum scheme: {checksum_scheme}. Expected: {CHECKSUM_SCHEME_RFC3961}"),
));
}
let checksum_type = from.read_u32::<LittleEndian>()?;
let checksum_value = Vec::decode(&mut from, message)?;
Ok(Self {
header_len,
checksum_scheme,
checksum_type,
checksum_value,
})
}
fn encode_with_payload(
&self,
offset: usize,
mut to: impl Write,
mut data: impl Write,
) -> Result<usize, Self::Error> {
to.write_u32::<LittleEndian>(self.header_len)?;
to.write_u32::<LittleEndian>(self.checksum_scheme)?;
to.write_u32::<LittleEndian>(self.checksum_type)?;
self.checksum_value.encode_with_payload(offset, &mut to, &mut data)
}
fn encode(&self, mut to: impl Write) -> Result<(), Self::Error> {
let offset = self.header_len as usize;
let mut header = Vec::new();
let mut data = Vec::new();
self.encode_with_payload(offset, &mut header, &mut data)?;
to.write_all(&header)?;
to.write_all(&data)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use uuid::Uuid;
use crate::constants::cksum_types::HMAC_SHA1_96_AES256;
use crate::negoex::NegoexDataType;
use crate::negoex::data_types::Guid;
use super::{CHECKSUM_SCHEME_RFC3961, Checksum, Extension, MessageHeader, MessageType, NEGOEXTS_MESSAGE_SIGNATURE};
#[test]
fn guid_encode() {
let guid = Uuid::from_str("0d53335c-f9ea-4d0d-b2ec-4ae3786ec308").unwrap();
let mut encoded = Vec::new();
guid.encode(&mut encoded).unwrap();
assert_eq!(
&[92, 51, 83, 13, 234, 249, 13, 77, 178, 236, 74, 227, 120, 110, 195, 8],
encoded.as_slice()
);
}
#[test]
fn guid_decode() {
let encoded_guid = [90, 7, 41, 59, 145, 243, 51, 175, 161, 180, 162, 18, 36, 157, 124, 180];
let guid = Guid::decode(&encoded_guid as &[u8], &encoded_guid).unwrap();
assert_eq!(Uuid::from_str("3b29075a-f391-af33-a1b4-a212249d7cb4").unwrap(), guid);
}
#[test]
fn message_type_decode() {
let encoded = [1, 0, 0, 0];
let message_type = MessageType::decode(&encoded as &[u8], &encoded).unwrap();
assert_eq!(MessageType::AcceptorNego, message_type);
}
#[test]
fn message_type_encode() {
let message_type = MessageType::ApRequest;
let mut encoded = Vec::new();
message_type.encode(&mut encoded).unwrap();
assert_eq!(&[5, 0, 0, 0], encoded.as_slice());
}
#[test]
fn message_header_encode() {
let message_header = MessageHeader {
signature: NEGOEXTS_MESSAGE_SIGNATURE,
message_type: MessageType::AcceptorNego,
sequence_num: 2,
header_len: 96,
message_len: 112,
conversation_id: Guid::from_str("3b29075a-f391-af33-a1b4-a212249d7cb4").unwrap(),
};
let mut encoded = Vec::new();
message_header.encode(&mut encoded).unwrap();
assert_eq!(
&[
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, 51, 175, 161, 180, 162, 18, 36, 157, 124, 180
],
encoded.as_slice(),
);
}
#[test]
fn message_header_decode() {
let encoded = [
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,
51, 175, 161, 180, 162, 18, 36, 157, 124, 180,
];
let message_header = MessageHeader::decode(&encoded as &[u8], &encoded).unwrap();
assert_eq!(
MessageHeader {
signature: NEGOEXTS_MESSAGE_SIGNATURE,
message_type: MessageType::AcceptorNego,
sequence_num: 2,
header_len: 96,
message_len: 112,
conversation_id: Guid::from_str("3b29075a-f391-af33-a1b4-a212249d7cb4").unwrap(),
},
message_header,
);
}
#[test]
fn extension_encode() {
let extension = Extension {
extension_type: 3,
extension_value: vec![1, 2, 3, 4, 5, 6],
};
let mut encoded = Vec::new();
extension.encode(&mut encoded).unwrap();
assert_eq!(
&[3, 0, 0, 0, 12, 0, 0, 0, 6, 0, 0, 0, 1, 2, 3, 4, 5, 6],
encoded.as_slice()
);
}
#[test]
fn extension_decode() {
let encoded = [3, 0, 0, 0, 12, 0, 0, 0, 6, 0, 0, 0, 1, 2, 3, 4, 5, 6];
let extension = Extension::decode(&encoded as &[u8], &encoded).unwrap();
assert_eq!(
Extension {
extension_type: 3,
extension_value: vec![1, 2, 3, 4, 5, 6],
},
extension
);
}
#[test]
fn checksum_decode() {
let negoex_verify = [
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,
51, 175, 161, 180, 162, 18, 36, 157, 124, 180, 92, 51, 83, 13, 234, 249, 13, 77, 178, 236, 74, 227, 120,
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,
148, 23, 131, 204, 12, 13, 36, 58, 87,
];
let checksum = Checksum::decode(&negoex_verify[56..], &negoex_verify).unwrap();
assert_eq!(
Checksum {
header_len: 20,
checksum_scheme: CHECKSUM_SCHEME_RFC3961,
checksum_type: HMAC_SHA1_96_AES256 as u32,
checksum_value: vec![228, 167, 112, 148, 23, 131, 204, 12, 13, 36, 58, 87],
},
checksum
);
}
#[test]
fn checksum_encode() {
let checksum = Checksum {
header_len: 20,
checksum_scheme: CHECKSUM_SCHEME_RFC3961,
checksum_type: HMAC_SHA1_96_AES256 as u32,
checksum_value: vec![228, 167, 112, 148, 23, 131, 204, 12, 13, 36, 58, 87],
};
let mut encoded = Vec::new();
checksum.encode(&mut encoded).unwrap();
assert_eq!(
&[
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,
13, 36, 58, 87
],
encoded.as_slice(),
)
}
}