stun_rs/attributes/stun/
nonce_cookie.rsuse crate::{attributes::stun::Nonce, StunError, StunErrorType};
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
use byteorder::{BigEndian, ByteOrder};
use enumflags2::{bitflags, BitFlags};
const NONCE_COOKIE_HEADER: &str = "obMatJos2";
#[bitflags]
#[repr(u32)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum StunSecurityFeatures {
PasswordAlgorithms = 1 << 31,
UserNameAnonymity = 1 << 30,
}
impl Nonce {
pub fn new_nonce_cookie<S>(
value: S,
flags: Option<BitFlags<StunSecurityFeatures>>,
) -> Result<Self, StunError>
where
S: AsRef<str>,
{
let features: u32 = match flags {
Some(flags) => flags.bits(),
None => 0,
};
let base64 = BASE64_STANDARD.encode(&features.to_be_bytes()[..3]);
let value = format!("{}{}{}", NONCE_COOKIE_HEADER, base64, value.as_ref());
Nonce::new(value)
}
pub fn is_nonce_cookie(&self) -> bool {
self.as_str().starts_with(NONCE_COOKIE_HEADER)
&& self.as_str().len() >= NONCE_COOKIE_HEADER.len() + 4
}
pub fn security_features(&self) -> Result<BitFlags<StunSecurityFeatures>, StunError> {
self.is_nonce_cookie()
.then_some(())
.ok_or_else(|| StunError::new(StunErrorType::InvalidParam, "Not nonce cookie"))?;
let flags = &self.as_str()[NONCE_COOKIE_HEADER.len()..NONCE_COOKIE_HEADER.len() + 4];
let mut bytes = [0x00; 4];
let size = BASE64_STANDARD
.decode_slice(flags, &mut bytes)
.map_err(|_e| {
StunError::new(
StunErrorType::InvalidParam,
"Error decoding base64 security features",
)
})?;
(size == 3).then_some(()).ok_or_else(|| {
StunError::new(
StunErrorType::InvalidParam,
"Unexpected security features lenght",
)
})?;
let val = BigEndian::read_u32(&bytes);
let flags =
BitFlags::<StunSecurityFeatures>::from_bits_truncate_c(val, BitFlags::CONST_TOKEN);
Ok(flags)
}
}
#[cfg(test)]
mod tests {
use enumflags2::make_bitflags;
use super::*;
#[test]
fn nonce_cookie() {
let value = "f//499k954d6OL34oL9FSTvy64sA";
let nonce = Nonce::new_nonce_cookie(value, None).expect("Can not create nonce cookie");
assert!(nonce.is_nonce_cookie());
let flags = nonce
.security_features()
.expect("Can not get feature flags");
assert!(!flags.contains(StunSecurityFeatures::PasswordAlgorithms));
assert!(!flags.contains(StunSecurityFeatures::UserNameAnonymity));
let flags: BitFlags<StunSecurityFeatures> =
make_bitflags!(StunSecurityFeatures::{PasswordAlgorithms});
let nonce =
Nonce::new_nonce_cookie(value, Some(flags)).expect("Can not create nonce cookie");
assert!(nonce.is_nonce_cookie());
let flags = nonce
.security_features()
.expect("Can not get feature flags");
assert!(flags.contains(StunSecurityFeatures::PasswordAlgorithms));
assert!(!flags.contains(StunSecurityFeatures::UserNameAnonymity));
let flags: BitFlags<StunSecurityFeatures> =
make_bitflags!(StunSecurityFeatures::{UserNameAnonymity});
let nonce =
Nonce::new_nonce_cookie(value, Some(flags)).expect("Can not create nonce cookie");
assert!(nonce.is_nonce_cookie());
let flags = nonce
.security_features()
.expect("Can not get feature flags");
assert!(!flags.contains(StunSecurityFeatures::PasswordAlgorithms));
assert!(flags.contains(StunSecurityFeatures::UserNameAnonymity));
let flags: BitFlags<StunSecurityFeatures> =
make_bitflags!(StunSecurityFeatures::{PasswordAlgorithms | UserNameAnonymity});
let nonce =
Nonce::new_nonce_cookie(value, Some(flags)).expect("Can not create nonce cookie");
assert!(nonce.is_nonce_cookie());
let flags = nonce
.security_features()
.expect("Can not get feature flags");
assert!(flags.contains(StunSecurityFeatures::PasswordAlgorithms));
assert!(flags.contains(StunSecurityFeatures::UserNameAnonymity));
let flags: BitFlags<StunSecurityFeatures> =
make_bitflags!(StunSecurityFeatures::{PasswordAlgorithms | UserNameAnonymity});
let nonce = Nonce::new_nonce_cookie("", Some(flags)).expect("Can not create nonce cookie");
assert!(nonce.is_nonce_cookie());
let flags = nonce
.security_features()
.expect("Can not get feature flags");
assert!(flags.contains(StunSecurityFeatures::PasswordAlgorithms));
assert!(flags.contains(StunSecurityFeatures::UserNameAnonymity));
}
#[test]
fn nonce_cookie_error() {
let value = String::from("f//499k954d6OL34oL9FSTvy64sA");
let nonce = Nonce::new(value).expect("Can not create a Nonce attribute");
assert!(!nonce.is_nonce_cookie());
let result = nonce.security_features();
assert_eq!(
result.expect_err("Error expected"),
StunErrorType::InvalidParam
);
let value = String::from("obMatJos2f//==8");
let nonce = Nonce::new(value).expect("Can not create a Nonce attribute");
assert!(nonce.is_nonce_cookie());
let result = nonce.security_features();
assert_eq!(
result.expect_err("Error expected"),
StunErrorType::InvalidParam
);
}
}