use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum SasMsgType {
EstablishContext = 0,
CompleteEstablishContext = 1,
ContextError = 4,
MessageInContext = 2,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IdentityToken {
Absent,
Anonymous,
PrincipalName(Vec<u8>),
X509CertChain(Vec<u8>),
DistinguishedName(Vec<u8>),
}
impl IdentityToken {
#[must_use]
pub const fn discriminator(&self) -> u32 {
match self {
Self::Absent => 0,
Self::Anonymous => 1,
Self::PrincipalName(_) => 2,
Self::X509CertChain(_) => 4,
Self::DistinguishedName(_) => 8,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EstablishContext {
pub client_context_id: u64,
pub authorization_token: Vec<u8>,
pub identity_token: IdentityToken,
pub client_authentication_token: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompleteEstablishContext {
pub client_context_id: u64,
pub context_stateful: bool,
pub final_context_token: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MessageInContext {
pub client_context_id: u64,
pub discard_context: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContextError {
pub client_context_id: u64,
pub major_status: u32,
pub minor_status: u32,
pub error_token: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SasMessage {
EstablishContext(EstablishContext),
CompleteEstablishContext(CompleteEstablishContext),
ContextError(ContextError),
MessageInContext(MessageInContext),
}
impl SasMessage {
#[must_use]
pub fn msg_type(&self) -> SasMsgType {
match self {
Self::EstablishContext(_) => SasMsgType::EstablishContext,
Self::CompleteEstablishContext(_) => SasMsgType::CompleteEstablishContext,
Self::ContextError(_) => SasMsgType::ContextError,
Self::MessageInContext(_) => SasMsgType::MessageInContext,
}
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn sas_msg_type_values_match_spec() {
assert_eq!(SasMsgType::EstablishContext as u32, 0);
assert_eq!(SasMsgType::CompleteEstablishContext as u32, 1);
assert_eq!(SasMsgType::MessageInContext as u32, 2);
assert_eq!(SasMsgType::ContextError as u32, 4);
}
#[test]
fn identity_token_discriminator_matches_spec() {
assert_eq!(IdentityToken::Absent.discriminator(), 0);
assert_eq!(IdentityToken::Anonymous.discriminator(), 1);
assert_eq!(
IdentityToken::PrincipalName(alloc::vec![]).discriminator(),
2
);
assert_eq!(
IdentityToken::X509CertChain(alloc::vec![]).discriminator(),
4
);
assert_eq!(
IdentityToken::DistinguishedName(alloc::vec![]).discriminator(),
8
);
}
#[test]
fn establish_context_holds_full_payload() {
let ec = EstablishContext {
client_context_id: 42,
authorization_token: alloc::vec![],
identity_token: IdentityToken::PrincipalName(b"alice@REALM".to_vec()),
client_authentication_token: alloc::vec![0xab, 0xcd],
};
let msg = SasMessage::EstablishContext(ec);
assert_eq!(msg.msg_type(), SasMsgType::EstablishContext);
}
#[test]
fn complete_context_indicates_stateful() {
let cc = CompleteEstablishContext {
client_context_id: 42,
context_stateful: true,
final_context_token: alloc::vec![],
};
let msg = SasMessage::CompleteEstablishContext(cc);
match msg {
SasMessage::CompleteEstablishContext(c) => assert!(c.context_stateful),
_ => panic!(),
}
}
#[test]
fn message_in_context_can_request_discard() {
let m = MessageInContext {
client_context_id: 42,
discard_context: true,
};
let msg = SasMessage::MessageInContext(m);
assert_eq!(msg.msg_type(), SasMsgType::MessageInContext);
}
#[test]
fn context_error_carries_gss_status() {
let e = ContextError {
client_context_id: 42,
major_status: 0x0007_0000, minor_status: 0,
error_token: alloc::vec![],
};
let msg = SasMessage::ContextError(e);
assert_eq!(msg.msg_type(), SasMsgType::ContextError);
}
}