pub mod scp02;
pub mod scp03;
use crate::model::ScpVariant;
pub enum ScpSession {
Scp03(scp03::Scp03State),
Scp02(scp02::Scp02State),
}
impl ScpSession {
#[must_use]
pub fn security_level(&self) -> u8 {
match self {
ScpSession::Scp03(s) => s.security_level(),
ScpSession::Scp02(s) => s.security_level(),
}
}
#[must_use]
pub fn i_param(&self) -> u8 {
match self {
ScpSession::Scp03(s) => s.i_param(),
ScpSession::Scp02(s) => s.i_param(),
}
}
#[must_use]
pub fn kvn(&self) -> u8 {
match self {
ScpSession::Scp03(s) => s.kvn(),
ScpSession::Scp02(s) => s.kvn(),
}
}
#[must_use]
pub fn protocol(&self) -> crate::report::ScpProtocol {
match self {
ScpSession::Scp03(_) => crate::report::ScpProtocol::Scp03,
ScpSession::Scp02(_) => crate::report::ScpProtocol::Scp02,
}
}
#[must_use]
pub fn session_id(&self) -> u64 {
match self {
ScpSession::Scp03(s) => u64::from(s.session().index()),
ScpSession::Scp02(s) => u64::from(s.session().index()),
}
}
}
#[must_use]
pub fn select(advertised: &[ScpVariant], force: Option<ScpVariant>) -> Option<ScpVariant> {
if let Some(forced) = force {
return Some(forced);
}
let best_scp03 = advertised
.iter()
.filter_map(|v| match v {
ScpVariant::Scp03 { i_param } if scp03::i_supported(*i_param) => Some(*i_param),
_ => None,
})
.max();
if let Some(i_param) = best_scp03 {
return Some(ScpVariant::Scp03 { i_param });
}
advertised.iter().find_map(|v| match v {
ScpVariant::Scp02 { i_param } => Some(ScpVariant::Scp02 { i_param: *i_param }),
ScpVariant::Scp03 { .. } => None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prefers_highest_scp03() {
let adv = [
ScpVariant::Scp02 { i_param: 0x55 },
ScpVariant::Scp03 { i_param: 0x30 },
ScpVariant::Scp03 { i_param: 0x70 },
];
assert_eq!(
select(&adv, None),
Some(ScpVariant::Scp03 { i_param: 0x70 })
);
}
#[test]
fn falls_back_to_scp02_when_no_scp03() {
let adv = [ScpVariant::Scp02 { i_param: 0x55 }];
assert_eq!(
select(&adv, None),
Some(ScpVariant::Scp02 { i_param: 0x55 })
);
}
#[test]
fn ignores_out_of_scope_scp03_i() {
let adv = [
ScpVariant::Scp03 { i_param: 0x04 },
ScpVariant::Scp02 { i_param: 0x15 },
];
assert_eq!(
select(&adv, None),
Some(ScpVariant::Scp02 { i_param: 0x15 })
);
}
#[test]
fn selects_s16_scp03() {
let adv = [
ScpVariant::Scp02 { i_param: 0x55 },
ScpVariant::Scp03 { i_param: 0x78 },
];
assert_eq!(
select(&adv, None),
Some(ScpVariant::Scp03 { i_param: 0x78 })
);
}
#[test]
fn selects_random_challenge_scp03() {
let adv = [
ScpVariant::Scp02 { i_param: 0x55 },
ScpVariant::Scp03 { i_param: 0x60 },
];
assert_eq!(
select(&adv, None),
Some(ScpVariant::Scp03 { i_param: 0x60 })
);
}
#[test]
fn force_overrides_selection() {
let adv = [ScpVariant::Scp03 { i_param: 0x70 }];
let forced = ScpVariant::Scp02 { i_param: 0x55 };
assert_eq!(select(&adv, Some(forced)), Some(forced));
}
#[test]
fn empty_yields_none() {
assert_eq!(select(&[], None), None);
}
}