1pub mod auth;
13mod engine;
14mod privacy;
15mod usm;
16
17pub use auth::{LocalizedKey, MasterKey, MasterKeys};
18pub use engine::{EngineCache, EngineState, TIME_WINDOW, parse_discovery_response};
19pub use engine::{
20 is_decryption_error_report, is_not_in_time_window_report, is_unknown_engine_id_report,
21 is_unknown_user_name_report, is_unsupported_sec_level_report, is_wrong_digest_report,
22};
23pub use privacy::{PrivKey, SaltCounter};
24pub use usm::UsmSecurityParams;
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct ParseProtocolError {
29 input: String,
30 kind: ProtocolKind,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34enum ProtocolKind {
35 Auth,
36 Priv,
37}
38
39impl std::fmt::Display for ParseProtocolError {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self.kind {
42 ProtocolKind::Auth => write!(
43 f,
44 "unknown authentication protocol '{}'; expected one of: MD5, SHA, SHA-224, SHA-256, SHA-384, SHA-512",
45 self.input
46 ),
47 ProtocolKind::Priv => write!(
48 f,
49 "unknown privacy protocol '{}'; expected one of: DES, AES, AES-128, AES-192, AES-256",
50 self.input
51 ),
52 }
53 }
54}
55
56impl std::error::Error for ParseProtocolError {}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61pub enum AuthProtocol {
62 Md5,
64 Sha1,
66 Sha224,
68 Sha256,
70 Sha384,
72 Sha512,
74}
75
76impl std::fmt::Display for AuthProtocol {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 match self {
79 Self::Md5 => write!(f, "MD5"),
80 Self::Sha1 => write!(f, "SHA"),
81 Self::Sha224 => write!(f, "SHA-224"),
82 Self::Sha256 => write!(f, "SHA-256"),
83 Self::Sha384 => write!(f, "SHA-384"),
84 Self::Sha512 => write!(f, "SHA-512"),
85 }
86 }
87}
88
89impl std::str::FromStr for AuthProtocol {
90 type Err = ParseProtocolError;
91
92 fn from_str(s: &str) -> Result<Self, Self::Err> {
93 match s.to_ascii_uppercase().as_str() {
94 "MD5" => Ok(Self::Md5),
95 "SHA" | "SHA1" | "SHA-1" => Ok(Self::Sha1),
96 "SHA224" | "SHA-224" => Ok(Self::Sha224),
97 "SHA256" | "SHA-256" => Ok(Self::Sha256),
98 "SHA384" | "SHA-384" => Ok(Self::Sha384),
99 "SHA512" | "SHA-512" => Ok(Self::Sha512),
100 _ => Err(ParseProtocolError {
101 input: s.to_string(),
102 kind: ProtocolKind::Auth,
103 }),
104 }
105 }
106}
107
108impl AuthProtocol {
109 pub fn digest_len(self) -> usize {
114 match self {
115 Self::Md5 => 16,
116 Self::Sha1 => 20,
117 Self::Sha224 => 28,
118 Self::Sha256 => 32,
119 Self::Sha384 => 48,
120 Self::Sha512 => 64,
121 }
122 }
123
124 pub fn mac_len(self) -> usize {
126 match self {
127 Self::Md5 | Self::Sha1 => 12, Self::Sha224 => 16, Self::Sha256 => 24, Self::Sha384 => 32, Self::Sha512 => 48, }
133 }
134
135 pub fn is_compatible_with(self, priv_protocol: PrivProtocol) -> bool {
161 self.digest_len() >= priv_protocol.key_len()
162 }
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
168pub enum PrivProtocol {
169 Des,
171 Aes128,
173 Aes192,
175 Aes256,
177}
178
179impl std::fmt::Display for PrivProtocol {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 match self {
182 Self::Des => write!(f, "DES"),
183 Self::Aes128 => write!(f, "AES"),
184 Self::Aes192 => write!(f, "AES-192"),
185 Self::Aes256 => write!(f, "AES-256"),
186 }
187 }
188}
189
190impl std::str::FromStr for PrivProtocol {
191 type Err = ParseProtocolError;
192
193 fn from_str(s: &str) -> Result<Self, Self::Err> {
194 match s.to_ascii_uppercase().as_str() {
195 "DES" => Ok(Self::Des),
196 "AES" | "AES128" | "AES-128" => Ok(Self::Aes128),
197 "AES192" | "AES-192" => Ok(Self::Aes192),
198 "AES256" | "AES-256" => Ok(Self::Aes256),
199 _ => Err(ParseProtocolError {
200 input: s.to_string(),
201 kind: ProtocolKind::Priv,
202 }),
203 }
204 }
205}
206
207impl PrivProtocol {
208 pub fn key_len(self) -> usize {
210 match self {
211 Self::Des => 16, Self::Aes128 => 16,
213 Self::Aes192 => 24,
214 Self::Aes256 => 32,
215 }
216 }
217
218 pub fn salt_len(self) -> usize {
220 8 }
222
223 pub fn min_auth_protocol(self) -> AuthProtocol {
233 match self {
234 Self::Des | Self::Aes128 => AuthProtocol::Md5,
235 Self::Aes192 => AuthProtocol::Sha224,
236 Self::Aes256 => AuthProtocol::Sha256,
237 }
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 #[test]
246 fn test_auth_protocol_compatibility_all_with_des() {
247 assert!(AuthProtocol::Md5.is_compatible_with(PrivProtocol::Des));
249 assert!(AuthProtocol::Sha1.is_compatible_with(PrivProtocol::Des));
250 assert!(AuthProtocol::Sha224.is_compatible_with(PrivProtocol::Des));
251 assert!(AuthProtocol::Sha256.is_compatible_with(PrivProtocol::Des));
252 assert!(AuthProtocol::Sha384.is_compatible_with(PrivProtocol::Des));
253 assert!(AuthProtocol::Sha512.is_compatible_with(PrivProtocol::Des));
254 }
255
256 #[test]
257 fn test_auth_protocol_compatibility_all_with_aes128() {
258 assert!(AuthProtocol::Md5.is_compatible_with(PrivProtocol::Aes128));
260 assert!(AuthProtocol::Sha1.is_compatible_with(PrivProtocol::Aes128));
261 assert!(AuthProtocol::Sha224.is_compatible_with(PrivProtocol::Aes128));
262 assert!(AuthProtocol::Sha256.is_compatible_with(PrivProtocol::Aes128));
263 assert!(AuthProtocol::Sha384.is_compatible_with(PrivProtocol::Aes128));
264 assert!(AuthProtocol::Sha512.is_compatible_with(PrivProtocol::Aes128));
265 }
266
267 #[test]
268 fn test_auth_protocol_compatibility_with_aes192() {
269 assert!(!AuthProtocol::Md5.is_compatible_with(PrivProtocol::Aes192)); assert!(!AuthProtocol::Sha1.is_compatible_with(PrivProtocol::Aes192)); assert!(AuthProtocol::Sha224.is_compatible_with(PrivProtocol::Aes192)); assert!(AuthProtocol::Sha256.is_compatible_with(PrivProtocol::Aes192)); assert!(AuthProtocol::Sha384.is_compatible_with(PrivProtocol::Aes192)); assert!(AuthProtocol::Sha512.is_compatible_with(PrivProtocol::Aes192)); }
277
278 #[test]
279 fn test_auth_protocol_compatibility_with_aes256() {
280 assert!(!AuthProtocol::Md5.is_compatible_with(PrivProtocol::Aes256)); assert!(!AuthProtocol::Sha1.is_compatible_with(PrivProtocol::Aes256)); assert!(!AuthProtocol::Sha224.is_compatible_with(PrivProtocol::Aes256)); assert!(AuthProtocol::Sha256.is_compatible_with(PrivProtocol::Aes256)); assert!(AuthProtocol::Sha384.is_compatible_with(PrivProtocol::Aes256)); assert!(AuthProtocol::Sha512.is_compatible_with(PrivProtocol::Aes256)); }
288
289 #[test]
290 fn test_priv_protocol_min_auth_protocol() {
291 assert_eq!(PrivProtocol::Des.min_auth_protocol(), AuthProtocol::Md5);
292 assert_eq!(PrivProtocol::Aes128.min_auth_protocol(), AuthProtocol::Md5);
293 assert_eq!(
294 PrivProtocol::Aes192.min_auth_protocol(),
295 AuthProtocol::Sha224
296 );
297 assert_eq!(
298 PrivProtocol::Aes256.min_auth_protocol(),
299 AuthProtocol::Sha256
300 );
301 }
302
303 #[test]
304 fn test_auth_protocol_display() {
305 assert_eq!(format!("{}", AuthProtocol::Md5), "MD5");
306 assert_eq!(format!("{}", AuthProtocol::Sha1), "SHA");
307 assert_eq!(format!("{}", AuthProtocol::Sha224), "SHA-224");
308 assert_eq!(format!("{}", AuthProtocol::Sha256), "SHA-256");
309 assert_eq!(format!("{}", AuthProtocol::Sha384), "SHA-384");
310 assert_eq!(format!("{}", AuthProtocol::Sha512), "SHA-512");
311 }
312
313 #[test]
314 fn test_auth_protocol_from_str() {
315 assert_eq!("MD5".parse::<AuthProtocol>().unwrap(), AuthProtocol::Md5);
316 assert_eq!("md5".parse::<AuthProtocol>().unwrap(), AuthProtocol::Md5);
317 assert_eq!("SHA".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
318 assert_eq!("sha1".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
319 assert_eq!("SHA-1".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
320 assert_eq!(
321 "sha-224".parse::<AuthProtocol>().unwrap(),
322 AuthProtocol::Sha224
323 );
324 assert_eq!(
325 "SHA256".parse::<AuthProtocol>().unwrap(),
326 AuthProtocol::Sha256
327 );
328 assert_eq!(
329 "SHA-256".parse::<AuthProtocol>().unwrap(),
330 AuthProtocol::Sha256
331 );
332 assert_eq!(
333 "sha384".parse::<AuthProtocol>().unwrap(),
334 AuthProtocol::Sha384
335 );
336 assert_eq!(
337 "SHA-512".parse::<AuthProtocol>().unwrap(),
338 AuthProtocol::Sha512
339 );
340
341 assert!("invalid".parse::<AuthProtocol>().is_err());
342 }
343
344 #[test]
345 fn test_priv_protocol_display() {
346 assert_eq!(format!("{}", PrivProtocol::Des), "DES");
347 assert_eq!(format!("{}", PrivProtocol::Aes128), "AES");
348 assert_eq!(format!("{}", PrivProtocol::Aes192), "AES-192");
349 assert_eq!(format!("{}", PrivProtocol::Aes256), "AES-256");
350 }
351
352 #[test]
353 fn test_priv_protocol_from_str() {
354 assert_eq!("DES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des);
355 assert_eq!("des".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des);
356 assert_eq!("AES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Aes128);
357 assert_eq!("aes".parse::<PrivProtocol>().unwrap(), PrivProtocol::Aes128);
358 assert_eq!(
359 "AES128".parse::<PrivProtocol>().unwrap(),
360 PrivProtocol::Aes128
361 );
362 assert_eq!(
363 "AES-128".parse::<PrivProtocol>().unwrap(),
364 PrivProtocol::Aes128
365 );
366 assert_eq!(
367 "aes192".parse::<PrivProtocol>().unwrap(),
368 PrivProtocol::Aes192
369 );
370 assert_eq!(
371 "AES-192".parse::<PrivProtocol>().unwrap(),
372 PrivProtocol::Aes192
373 );
374 assert_eq!(
375 "aes256".parse::<PrivProtocol>().unwrap(),
376 PrivProtocol::Aes256
377 );
378 assert_eq!(
379 "AES-256".parse::<PrivProtocol>().unwrap(),
380 PrivProtocol::Aes256
381 );
382
383 assert!("invalid".parse::<PrivProtocol>().is_err());
384 }
385
386 #[test]
387 fn test_parse_protocol_error_display() {
388 let err = "bogus".parse::<AuthProtocol>().unwrap_err();
389 assert!(err.to_string().contains("bogus"));
390 assert!(err.to_string().contains("authentication protocol"));
391
392 let err = "bogus".parse::<PrivProtocol>().unwrap_err();
393 assert!(err.to_string().contains("bogus"));
394 assert!(err.to_string().contains("privacy protocol"));
395 }
396}