1pub mod scp02;
15pub mod scp03;
16
17use crate::model::ScpVariant;
18
19pub enum ScpSession {
21 Scp03(scp03::Scp03State),
22 Scp02(scp02::Scp02State),
23}
24
25impl ScpSession {
26 #[must_use]
28 pub fn security_level(&self) -> u8 {
29 match self {
30 ScpSession::Scp03(s) => s.security_level(),
31 ScpSession::Scp02(s) => s.security_level(),
32 }
33 }
34
35 #[must_use]
37 pub fn i_param(&self) -> u8 {
38 match self {
39 ScpSession::Scp03(s) => s.i_param(),
40 ScpSession::Scp02(s) => s.i_param(),
41 }
42 }
43
44 #[must_use]
46 pub fn kvn(&self) -> u8 {
47 match self {
48 ScpSession::Scp03(s) => s.kvn(),
49 ScpSession::Scp02(s) => s.kvn(),
50 }
51 }
52
53 #[must_use]
55 pub fn protocol(&self) -> crate::report::ScpProtocol {
56 match self {
57 ScpSession::Scp03(_) => crate::report::ScpProtocol::Scp03,
58 ScpSession::Scp02(_) => crate::report::ScpProtocol::Scp02,
59 }
60 }
61
62 #[must_use]
65 pub fn session_id(&self) -> u64 {
66 match self {
67 ScpSession::Scp03(s) => u64::from(s.session().index()),
68 ScpSession::Scp02(s) => u64::from(s.session().index()),
69 }
70 }
71}
72
73#[must_use]
80pub fn select(advertised: &[ScpVariant], force: Option<ScpVariant>) -> Option<ScpVariant> {
81 if let Some(forced) = force {
82 return Some(forced);
83 }
84 let best_scp03 = advertised
86 .iter()
87 .filter_map(|v| match v {
88 ScpVariant::Scp03 { i_param } if scp03::i_supported(*i_param) => Some(*i_param),
89 _ => None,
90 })
91 .max();
92 if let Some(i_param) = best_scp03 {
93 return Some(ScpVariant::Scp03 { i_param });
94 }
95 advertised.iter().find_map(|v| match v {
97 ScpVariant::Scp02 { i_param } => Some(ScpVariant::Scp02 { i_param: *i_param }),
98 ScpVariant::Scp03 { .. } => None,
99 })
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn prefers_highest_scp03() {
108 let adv = [
109 ScpVariant::Scp02 { i_param: 0x55 },
110 ScpVariant::Scp03 { i_param: 0x30 },
111 ScpVariant::Scp03 { i_param: 0x70 },
112 ];
113 assert_eq!(
114 select(&adv, None),
115 Some(ScpVariant::Scp03 { i_param: 0x70 })
116 );
117 }
118
119 #[test]
120 fn falls_back_to_scp02_when_no_scp03() {
121 let adv = [ScpVariant::Scp02 { i_param: 0x55 }];
122 assert_eq!(
123 select(&adv, None),
124 Some(ScpVariant::Scp02 { i_param: 0x55 })
125 );
126 }
127
128 #[test]
129 fn ignores_out_of_scope_scp03_i() {
130 let adv = [
133 ScpVariant::Scp03 { i_param: 0x04 },
134 ScpVariant::Scp02 { i_param: 0x15 },
135 ];
136 assert_eq!(
137 select(&adv, None),
138 Some(ScpVariant::Scp02 { i_param: 0x15 })
139 );
140 }
141
142 #[test]
143 fn selects_s16_scp03() {
144 let adv = [
146 ScpVariant::Scp02 { i_param: 0x55 },
147 ScpVariant::Scp03 { i_param: 0x78 },
148 ];
149 assert_eq!(
150 select(&adv, None),
151 Some(ScpVariant::Scp03 { i_param: 0x78 })
152 );
153 }
154
155 #[test]
156 fn selects_random_challenge_scp03() {
157 let adv = [
159 ScpVariant::Scp02 { i_param: 0x55 },
160 ScpVariant::Scp03 { i_param: 0x60 },
161 ];
162 assert_eq!(
163 select(&adv, None),
164 Some(ScpVariant::Scp03 { i_param: 0x60 })
165 );
166 }
167
168 #[test]
169 fn force_overrides_selection() {
170 let adv = [ScpVariant::Scp03 { i_param: 0x70 }];
171 let forced = ScpVariant::Scp02 { i_param: 0x55 };
172 assert_eq!(select(&adv, Some(forced)), Some(forced));
173 }
174
175 #[test]
176 fn empty_yields_none() {
177 assert_eq!(select(&[], None), None);
178 }
179}