1pub mod auth;
13mod engine;
14mod privacy;
15mod usm;
16
17pub use auth::{LocalizedKey, MasterKey, MasterKeys, extend_key};
18pub use engine::{
19 DEFAULT_MSG_MAX_SIZE, EngineCache, EngineState, TIME_WINDOW, parse_discovery_response,
20 parse_discovery_response_with_limits,
21};
22pub use engine::{
23 is_decryption_error_report, is_not_in_time_window_report, is_unknown_engine_id_report,
24 is_unknown_user_name_report, is_unsupported_sec_level_report, is_wrong_digest_report,
25};
26pub use privacy::{PrivKey, SaltCounter};
27pub use usm::UsmSecurityParams;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
36pub enum KeyExtension {
37 #[default]
40 None,
41 Blumenthal,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct ParseProtocolError {
49 input: String,
50 kind: ProtocolKind,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54enum ProtocolKind {
55 Auth,
56 Priv,
57}
58
59impl std::fmt::Display for ParseProtocolError {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match self.kind {
62 ProtocolKind::Auth => write!(
63 f,
64 "unknown authentication protocol '{}'; expected one of: MD5, SHA, SHA-224, SHA-256, SHA-384, SHA-512",
65 self.input
66 ),
67 ProtocolKind::Priv => write!(
68 f,
69 "unknown privacy protocol '{}'; expected one of: DES, AES, AES-128, AES-192, AES-256",
70 self.input
71 ),
72 }
73 }
74}
75
76impl std::error::Error for ParseProtocolError {}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81pub enum AuthProtocol {
82 Md5,
84 Sha1,
86 Sha224,
88 Sha256,
90 Sha384,
92 Sha512,
94}
95
96impl std::fmt::Display for AuthProtocol {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match self {
99 Self::Md5 => write!(f, "MD5"),
100 Self::Sha1 => write!(f, "SHA"),
101 Self::Sha224 => write!(f, "SHA-224"),
102 Self::Sha256 => write!(f, "SHA-256"),
103 Self::Sha384 => write!(f, "SHA-384"),
104 Self::Sha512 => write!(f, "SHA-512"),
105 }
106 }
107}
108
109impl std::str::FromStr for AuthProtocol {
110 type Err = ParseProtocolError;
111
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
113 match s.to_ascii_uppercase().as_str() {
114 "MD5" => Ok(Self::Md5),
115 "SHA" | "SHA1" | "SHA-1" => Ok(Self::Sha1),
116 "SHA224" | "SHA-224" => Ok(Self::Sha224),
117 "SHA256" | "SHA-256" => Ok(Self::Sha256),
118 "SHA384" | "SHA-384" => Ok(Self::Sha384),
119 "SHA512" | "SHA-512" => Ok(Self::Sha512),
120 _ => Err(ParseProtocolError {
121 input: s.to_string(),
122 kind: ProtocolKind::Auth,
123 }),
124 }
125 }
126}
127
128impl AuthProtocol {
129 pub fn digest_len(self) -> usize {
134 match self {
135 Self::Md5 => 16,
136 Self::Sha1 => 20,
137 Self::Sha224 => 28,
138 Self::Sha256 => 32,
139 Self::Sha384 => 48,
140 Self::Sha512 => 64,
141 }
142 }
143
144 pub fn mac_len(self) -> usize {
146 match self {
147 Self::Md5 | Self::Sha1 => 12, Self::Sha224 => 16, Self::Sha256 => 24, Self::Sha384 => 32, Self::Sha512 => 48, }
153 }
154
155 pub fn is_compatible_with(self, priv_protocol: PrivProtocol) -> bool {
188 self.digest_len() >= priv_protocol.key_len()
189 }
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
195pub enum PrivProtocol {
196 Des,
198 Aes128,
200 Aes192,
202 Aes256,
204}
205
206impl std::fmt::Display for PrivProtocol {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 match self {
209 Self::Des => write!(f, "DES"),
210 Self::Aes128 => write!(f, "AES"),
211 Self::Aes192 => write!(f, "AES-192"),
212 Self::Aes256 => write!(f, "AES-256"),
213 }
214 }
215}
216
217impl std::str::FromStr for PrivProtocol {
218 type Err = ParseProtocolError;
219
220 fn from_str(s: &str) -> Result<Self, Self::Err> {
221 match s.to_ascii_uppercase().as_str() {
222 "DES" => Ok(Self::Des),
223 "AES" | "AES128" | "AES-128" => Ok(Self::Aes128),
224 "AES192" | "AES-192" => Ok(Self::Aes192),
225 "AES256" | "AES-256" => Ok(Self::Aes256),
226 _ => Err(ParseProtocolError {
227 input: s.to_string(),
228 kind: ProtocolKind::Priv,
229 }),
230 }
231 }
232}
233
234impl PrivProtocol {
235 pub fn key_len(self) -> usize {
237 match self {
238 Self::Des => 16, Self::Aes128 => 16,
240 Self::Aes192 => 24,
241 Self::Aes256 => 32,
242 }
243 }
244
245 pub fn salt_len(self) -> usize {
247 8 }
249
250 pub fn min_auth_protocol(self) -> AuthProtocol {
260 match self {
261 Self::Des | Self::Aes128 => AuthProtocol::Md5,
262 Self::Aes192 => AuthProtocol::Sha224,
263 Self::Aes256 => AuthProtocol::Sha256,
264 }
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_auth_protocol_compatibility_all_with_des() {
274 assert!(AuthProtocol::Md5.is_compatible_with(PrivProtocol::Des));
276 assert!(AuthProtocol::Sha1.is_compatible_with(PrivProtocol::Des));
277 assert!(AuthProtocol::Sha224.is_compatible_with(PrivProtocol::Des));
278 assert!(AuthProtocol::Sha256.is_compatible_with(PrivProtocol::Des));
279 assert!(AuthProtocol::Sha384.is_compatible_with(PrivProtocol::Des));
280 assert!(AuthProtocol::Sha512.is_compatible_with(PrivProtocol::Des));
281 }
282
283 #[test]
284 fn test_auth_protocol_compatibility_all_with_aes128() {
285 assert!(AuthProtocol::Md5.is_compatible_with(PrivProtocol::Aes128));
287 assert!(AuthProtocol::Sha1.is_compatible_with(PrivProtocol::Aes128));
288 assert!(AuthProtocol::Sha224.is_compatible_with(PrivProtocol::Aes128));
289 assert!(AuthProtocol::Sha256.is_compatible_with(PrivProtocol::Aes128));
290 assert!(AuthProtocol::Sha384.is_compatible_with(PrivProtocol::Aes128));
291 assert!(AuthProtocol::Sha512.is_compatible_with(PrivProtocol::Aes128));
292 }
293
294 #[test]
295 fn test_auth_protocol_compatibility_with_aes192() {
296 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)); }
304
305 #[test]
306 fn test_auth_protocol_compatibility_with_aes256() {
307 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)); }
315
316 #[test]
317 fn test_priv_protocol_min_auth_protocol() {
318 assert_eq!(PrivProtocol::Des.min_auth_protocol(), AuthProtocol::Md5);
319 assert_eq!(PrivProtocol::Aes128.min_auth_protocol(), AuthProtocol::Md5);
320 assert_eq!(
321 PrivProtocol::Aes192.min_auth_protocol(),
322 AuthProtocol::Sha224
323 );
324 assert_eq!(
325 PrivProtocol::Aes256.min_auth_protocol(),
326 AuthProtocol::Sha256
327 );
328 }
329
330 #[test]
331 fn test_auth_protocol_display() {
332 assert_eq!(format!("{}", AuthProtocol::Md5), "MD5");
333 assert_eq!(format!("{}", AuthProtocol::Sha1), "SHA");
334 assert_eq!(format!("{}", AuthProtocol::Sha224), "SHA-224");
335 assert_eq!(format!("{}", AuthProtocol::Sha256), "SHA-256");
336 assert_eq!(format!("{}", AuthProtocol::Sha384), "SHA-384");
337 assert_eq!(format!("{}", AuthProtocol::Sha512), "SHA-512");
338 }
339
340 #[test]
341 fn test_auth_protocol_from_str() {
342 assert_eq!("MD5".parse::<AuthProtocol>().unwrap(), AuthProtocol::Md5);
343 assert_eq!("md5".parse::<AuthProtocol>().unwrap(), AuthProtocol::Md5);
344 assert_eq!("SHA".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
345 assert_eq!("sha1".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
346 assert_eq!("SHA-1".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
347 assert_eq!(
348 "sha-224".parse::<AuthProtocol>().unwrap(),
349 AuthProtocol::Sha224
350 );
351 assert_eq!(
352 "SHA256".parse::<AuthProtocol>().unwrap(),
353 AuthProtocol::Sha256
354 );
355 assert_eq!(
356 "SHA-256".parse::<AuthProtocol>().unwrap(),
357 AuthProtocol::Sha256
358 );
359 assert_eq!(
360 "sha384".parse::<AuthProtocol>().unwrap(),
361 AuthProtocol::Sha384
362 );
363 assert_eq!(
364 "SHA-512".parse::<AuthProtocol>().unwrap(),
365 AuthProtocol::Sha512
366 );
367
368 assert!("invalid".parse::<AuthProtocol>().is_err());
369 }
370
371 #[test]
372 fn test_priv_protocol_display() {
373 assert_eq!(format!("{}", PrivProtocol::Des), "DES");
374 assert_eq!(format!("{}", PrivProtocol::Aes128), "AES");
375 assert_eq!(format!("{}", PrivProtocol::Aes192), "AES-192");
376 assert_eq!(format!("{}", PrivProtocol::Aes256), "AES-256");
377 }
378
379 #[test]
380 fn test_priv_protocol_from_str() {
381 assert_eq!("DES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des);
382 assert_eq!("des".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des);
383 assert_eq!("AES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Aes128);
384 assert_eq!("aes".parse::<PrivProtocol>().unwrap(), PrivProtocol::Aes128);
385 assert_eq!(
386 "AES128".parse::<PrivProtocol>().unwrap(),
387 PrivProtocol::Aes128
388 );
389 assert_eq!(
390 "AES-128".parse::<PrivProtocol>().unwrap(),
391 PrivProtocol::Aes128
392 );
393 assert_eq!(
394 "aes192".parse::<PrivProtocol>().unwrap(),
395 PrivProtocol::Aes192
396 );
397 assert_eq!(
398 "AES-192".parse::<PrivProtocol>().unwrap(),
399 PrivProtocol::Aes192
400 );
401 assert_eq!(
402 "aes256".parse::<PrivProtocol>().unwrap(),
403 PrivProtocol::Aes256
404 );
405 assert_eq!(
406 "AES-256".parse::<PrivProtocol>().unwrap(),
407 PrivProtocol::Aes256
408 );
409
410 assert!("invalid".parse::<PrivProtocol>().is_err());
411 }
412
413 #[test]
414 fn test_parse_protocol_error_display() {
415 let err = "bogus".parse::<AuthProtocol>().unwrap_err();
416 assert!(err.to_string().contains("bogus"));
417 assert!(err.to_string().contains("authentication protocol"));
418
419 let err = "bogus".parse::<PrivProtocol>().unwrap_err();
420 assert!(err.to_string().contains("bogus"));
421 assert!(err.to_string().contains("privacy protocol"));
422 }
423}