1pub mod auth;
13mod engine;
14mod privacy;
15mod usm;
16
17pub use auth::{LocalizedKey, MasterKey, MasterKeys};
18pub use engine::{
19 DEFAULT_MSG_MAX_SIZE, EngineCache, EngineState, MAX_ENGINE_TIME, TIME_WINDOW,
20 parse_discovery_response, 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, PrivacyError, PrivacyResult, SaltCounter};
27pub use usm::UsmSecurityParams;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
35pub(crate) enum KeyExtension {
36 #[default]
38 None,
39 Blumenthal,
41 Reeder,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct ParseProtocolError {
48 input: String,
49 kind: ProtocolKind,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53enum ProtocolKind {
54 Auth,
55 Priv,
56}
57
58impl std::fmt::Display for ParseProtocolError {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self.kind {
61 ProtocolKind::Auth => write!(
62 f,
63 "unknown authentication protocol '{}'; expected one of: MD5, SHA, SHA-224, SHA-256, SHA-384, SHA-512",
64 self.input
65 ),
66 ProtocolKind::Priv => write!(
67 f,
68 "unknown privacy protocol '{}'; expected one of: DES, AES, AES-128, AES-192, AES-256",
69 self.input
70 ),
71 }
72 }
73}
74
75impl std::error::Error for ParseProtocolError {}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub enum AuthProtocol {
81 Md5,
83 Sha1,
85 Sha224,
87 Sha256,
89 Sha384,
91 Sha512,
93}
94
95impl std::fmt::Display for AuthProtocol {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 Self::Md5 => write!(f, "MD5"),
99 Self::Sha1 => write!(f, "SHA"),
100 Self::Sha224 => write!(f, "SHA-224"),
101 Self::Sha256 => write!(f, "SHA-256"),
102 Self::Sha384 => write!(f, "SHA-384"),
103 Self::Sha512 => write!(f, "SHA-512"),
104 }
105 }
106}
107
108impl std::str::FromStr for AuthProtocol {
109 type Err = ParseProtocolError;
110
111 fn from_str(s: &str) -> Result<Self, Self::Err> {
112 match s.to_ascii_uppercase().as_str() {
113 "MD5" => Ok(Self::Md5),
114 "SHA" | "SHA1" | "SHA-1" => Ok(Self::Sha1),
115 "SHA224" | "SHA-224" => Ok(Self::Sha224),
116 "SHA256" | "SHA-256" => Ok(Self::Sha256),
117 "SHA384" | "SHA-384" => Ok(Self::Sha384),
118 "SHA512" | "SHA-512" => Ok(Self::Sha512),
119 _ => Err(ParseProtocolError {
120 input: s.to_string(),
121 kind: ProtocolKind::Auth,
122 }),
123 }
124 }
125}
126
127impl AuthProtocol {
128 pub fn digest_len(self) -> usize {
133 match self {
134 Self::Md5 => 16,
135 Self::Sha1 => 20,
136 Self::Sha224 => 28,
137 Self::Sha256 => 32,
138 Self::Sha384 => 48,
139 Self::Sha512 => 64,
140 }
141 }
142
143 pub fn mac_len(self) -> usize {
145 match self {
146 Self::Md5 | Self::Sha1 => 12, Self::Sha224 => 16, Self::Sha256 => 24, Self::Sha384 => 32, Self::Sha512 => 48, }
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
158pub enum PrivProtocol {
159 Des,
164 Des3,
169 Aes128,
171 Aes192,
173 Aes256,
175}
176
177impl std::fmt::Display for PrivProtocol {
178 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179 match self {
180 Self::Des => write!(f, "DES"),
181 Self::Des3 => write!(f, "3DES"),
182 Self::Aes128 => write!(f, "AES"),
183 Self::Aes192 => write!(f, "AES-192"),
184 Self::Aes256 => write!(f, "AES-256"),
185 }
186 }
187}
188
189impl std::str::FromStr for PrivProtocol {
190 type Err = ParseProtocolError;
191
192 fn from_str(s: &str) -> Result<Self, Self::Err> {
193 match s.to_ascii_uppercase().as_str() {
194 "DES" => Ok(Self::Des),
195 "3DES" | "3DES-EDE" | "DES3" | "TDES" => Ok(Self::Des3),
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::Des3 => 32, Self::Aes128 => 16,
214 Self::Aes192 => 24,
215 Self::Aes256 => 32,
216 }
217 }
218
219 pub fn salt_len(self) -> usize {
221 8 }
223
224 pub(crate) fn key_extension_for(self, auth_protocol: AuthProtocol) -> KeyExtension {
233 let auth_len = auth_protocol.digest_len();
234 let priv_len = self.key_len();
235
236 if auth_len >= priv_len {
237 return KeyExtension::None;
238 }
239
240 match self {
241 Self::Des3 => KeyExtension::Reeder,
242 Self::Aes192 | Self::Aes256 => KeyExtension::Blumenthal,
243 Self::Des | Self::Aes128 => KeyExtension::None, }
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn test_auth_protocol_display() {
254 assert_eq!(format!("{}", AuthProtocol::Md5), "MD5");
255 assert_eq!(format!("{}", AuthProtocol::Sha1), "SHA");
256 assert_eq!(format!("{}", AuthProtocol::Sha224), "SHA-224");
257 assert_eq!(format!("{}", AuthProtocol::Sha256), "SHA-256");
258 assert_eq!(format!("{}", AuthProtocol::Sha384), "SHA-384");
259 assert_eq!(format!("{}", AuthProtocol::Sha512), "SHA-512");
260 }
261
262 #[test]
263 fn test_auth_protocol_from_str() {
264 assert_eq!("MD5".parse::<AuthProtocol>().unwrap(), AuthProtocol::Md5);
265 assert_eq!("md5".parse::<AuthProtocol>().unwrap(), AuthProtocol::Md5);
266 assert_eq!("SHA".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
267 assert_eq!("sha1".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
268 assert_eq!("SHA-1".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
269 assert_eq!(
270 "sha-224".parse::<AuthProtocol>().unwrap(),
271 AuthProtocol::Sha224
272 );
273 assert_eq!(
274 "SHA256".parse::<AuthProtocol>().unwrap(),
275 AuthProtocol::Sha256
276 );
277 assert_eq!(
278 "SHA-256".parse::<AuthProtocol>().unwrap(),
279 AuthProtocol::Sha256
280 );
281 assert_eq!(
282 "sha384".parse::<AuthProtocol>().unwrap(),
283 AuthProtocol::Sha384
284 );
285 assert_eq!(
286 "SHA-512".parse::<AuthProtocol>().unwrap(),
287 AuthProtocol::Sha512
288 );
289
290 assert!("invalid".parse::<AuthProtocol>().is_err());
291 }
292
293 #[test]
294 fn test_priv_protocol_display() {
295 assert_eq!(format!("{}", PrivProtocol::Des), "DES");
296 assert_eq!(format!("{}", PrivProtocol::Des3), "3DES");
297 assert_eq!(format!("{}", PrivProtocol::Aes128), "AES");
298 assert_eq!(format!("{}", PrivProtocol::Aes192), "AES-192");
299 assert_eq!(format!("{}", PrivProtocol::Aes256), "AES-256");
300 }
301
302 #[test]
303 fn test_priv_protocol_from_str() {
304 assert_eq!("DES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des);
305 assert_eq!("des".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des);
306 assert_eq!("3DES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des3);
307 assert_eq!("3des".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des3);
308 assert_eq!(
309 "3DES-EDE".parse::<PrivProtocol>().unwrap(),
310 PrivProtocol::Des3
311 );
312 assert_eq!("DES3".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des3);
313 assert_eq!("TDES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des3);
314 assert_eq!("AES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Aes128);
315 assert_eq!("aes".parse::<PrivProtocol>().unwrap(), PrivProtocol::Aes128);
316 assert_eq!(
317 "AES128".parse::<PrivProtocol>().unwrap(),
318 PrivProtocol::Aes128
319 );
320 assert_eq!(
321 "AES-128".parse::<PrivProtocol>().unwrap(),
322 PrivProtocol::Aes128
323 );
324 assert_eq!(
325 "aes192".parse::<PrivProtocol>().unwrap(),
326 PrivProtocol::Aes192
327 );
328 assert_eq!(
329 "AES-192".parse::<PrivProtocol>().unwrap(),
330 PrivProtocol::Aes192
331 );
332 assert_eq!(
333 "aes256".parse::<PrivProtocol>().unwrap(),
334 PrivProtocol::Aes256
335 );
336 assert_eq!(
337 "AES-256".parse::<PrivProtocol>().unwrap(),
338 PrivProtocol::Aes256
339 );
340
341 assert!("invalid".parse::<PrivProtocol>().is_err());
342 }
343
344 #[test]
345 fn test_parse_protocol_error_display() {
346 let err = "bogus".parse::<AuthProtocol>().unwrap_err();
347 assert!(err.to_string().contains("bogus"));
348 assert!(err.to_string().contains("authentication protocol"));
349
350 let err = "bogus".parse::<PrivProtocol>().unwrap_err();
351 assert!(err.to_string().contains("bogus"));
352 assert!(err.to_string().contains("privacy protocol"));
353 }
354}