use crate::security::tag::AuthenticationTag;
use crate::types::Symbol;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthenticatedSymbol {
symbol: Symbol,
tag: AuthenticationTag,
verified: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthenticatedSymbolState {
Verified,
UnverifiedTagged,
UnauthenticatedSentinel,
}
impl AuthenticatedSymbol {
#[must_use]
pub(crate) fn new_verified(symbol: Symbol, tag: AuthenticationTag) -> Self {
let verified = !tag.is_zero();
Self {
symbol,
tag,
verified,
}
}
#[must_use]
pub(crate) fn new_unauthenticated(symbol: Symbol) -> Self {
Self {
symbol,
tag: AuthenticationTag::zero(),
verified: false,
}
}
#[must_use]
pub fn from_parts(symbol: Symbol, tag: AuthenticationTag) -> Self {
Self {
symbol,
tag,
verified: false,
}
}
#[must_use]
#[inline]
pub fn symbol(&self) -> &Symbol {
&self.symbol
}
#[must_use]
#[inline]
pub fn tag(&self) -> &AuthenticationTag {
&self.tag
}
#[must_use]
#[inline]
pub fn is_verified(&self) -> bool {
self.verified
}
#[must_use]
#[inline]
pub fn authentication_state(&self) -> AuthenticatedSymbolState {
if self.verified {
AuthenticatedSymbolState::Verified
} else if self.tag.is_zero() {
AuthenticatedSymbolState::UnauthenticatedSentinel
} else {
AuthenticatedSymbolState::UnverifiedTagged
}
}
pub(super) fn set_verified(&mut self, verified: bool) {
self.verified = verified;
}
#[must_use]
pub fn into_symbol(self) -> Symbol {
self.symbol
}
}
#[cfg(test)]
mod tests {
#![allow(
clippy::pedantic,
clippy::nursery,
clippy::expect_fun_call,
clippy::map_unwrap_or,
clippy::cast_possible_wrap,
clippy::future_not_send
)]
use super::*;
use crate::security::AuthKey;
use crate::types::{SymbolId, SymbolKind};
fn real_tag(symbol: &Symbol) -> AuthenticationTag {
let key = AuthKey::from_seed(0xDEADBEEF);
AuthenticationTag::compute(&key, symbol)
}
#[test]
fn test_new_verified() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::new(id, vec![], SymbolKind::Source);
let tag = real_tag(&symbol);
let auth = AuthenticatedSymbol::new_verified(symbol.clone(), tag);
assert!(auth.is_verified());
assert_eq!(auth.symbol(), &symbol);
assert_eq!(auth.tag(), &tag);
}
#[test]
fn test_new_verified_zero_tag_forces_unverified() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::new(id, vec![], SymbolKind::Source);
let auth = AuthenticatedSymbol::new_verified(symbol, AuthenticationTag::zero());
assert!(
!auth.is_verified(),
"br-asupersync-srqosl: zero-tag sentinel must not pose as verified"
);
}
#[test]
fn test_new_unauthenticated() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::new(id, vec![1, 2, 3], SymbolKind::Source);
let auth = AuthenticatedSymbol::new_unauthenticated(symbol.clone());
assert!(
!auth.is_verified(),
"unauthenticated symbol must not be verified"
);
assert!(
auth.tag().is_zero(),
"unauthenticated symbol must use zero tag"
);
assert_eq!(auth.symbol(), &symbol, "symbol data must be preserved");
}
#[test]
fn test_from_parts_unverified() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::new(id, vec![], SymbolKind::Source);
let tag = AuthenticationTag::zero();
let auth = AuthenticatedSymbol::from_parts(symbol, tag);
assert!(!auth.is_verified());
}
#[test]
fn test_into_symbol() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::new(id, vec![1, 2], SymbolKind::Source);
let tag = real_tag(&symbol);
let auth = AuthenticatedSymbol::new_verified(symbol.clone(), tag);
let unwrapped = auth.into_symbol();
assert_eq!(unwrapped, symbol);
}
#[test]
fn authenticated_symbol_debug_clone_eq() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::new(id, vec![1, 2, 3], SymbolKind::Source);
let tag = real_tag(&symbol);
let auth = AuthenticatedSymbol::new_verified(symbol, tag);
let dbg = format!("{auth:?}");
assert!(dbg.contains("AuthenticatedSymbol"), "{dbg}");
let cloned = auth.clone();
assert_eq!(auth, cloned);
}
#[test]
fn set_verified_updates_flag() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::new(id, vec![1, 2, 3], SymbolKind::Source);
let tag = real_tag(&symbol);
let mut auth = AuthenticatedSymbol::new_verified(symbol, tag);
auth.set_verified(false);
assert!(!auth.is_verified());
auth.set_verified(true);
assert!(auth.is_verified());
}
#[test]
fn test_set_verified_visibility_restriction() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::new(id, vec![1, 2, 3], SymbolKind::Source);
let tag = real_tag(&symbol);
let mut auth = AuthenticatedSymbol::new_verified(symbol, tag);
auth.set_verified(false);
assert!(!auth.is_verified());
}
}