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)]
79pub enum AuthProtocol {
80 Md5,
82 Sha1,
84 Sha224,
86 Sha256,
88 Sha384,
90 Sha512,
92}
93
94impl std::fmt::Display for AuthProtocol {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 Self::Md5 => write!(f, "MD5"),
98 Self::Sha1 => write!(f, "SHA"),
99 Self::Sha224 => write!(f, "SHA-224"),
100 Self::Sha256 => write!(f, "SHA-256"),
101 Self::Sha384 => write!(f, "SHA-384"),
102 Self::Sha512 => write!(f, "SHA-512"),
103 }
104 }
105}
106
107impl std::str::FromStr for AuthProtocol {
108 type Err = ParseProtocolError;
109
110 fn from_str(s: &str) -> Result<Self, Self::Err> {
111 match s.to_ascii_uppercase().as_str() {
112 "MD5" => Ok(Self::Md5),
113 "SHA" | "SHA1" | "SHA-1" => Ok(Self::Sha1),
114 "SHA224" | "SHA-224" => Ok(Self::Sha224),
115 "SHA256" | "SHA-256" => Ok(Self::Sha256),
116 "SHA384" | "SHA-384" => Ok(Self::Sha384),
117 "SHA512" | "SHA-512" => Ok(Self::Sha512),
118 _ => Err(ParseProtocolError {
119 input: s.to_string(),
120 kind: ProtocolKind::Auth,
121 }),
122 }
123 }
124}
125
126impl AuthProtocol {
127 pub fn digest_len(self) -> usize {
132 match self {
133 Self::Md5 => 16,
134 Self::Sha1 => 20,
135 Self::Sha224 => 28,
136 Self::Sha256 => 32,
137 Self::Sha384 => 48,
138 Self::Sha512 => 64,
139 }
140 }
141
142 pub fn mac_len(self) -> usize {
144 match self {
145 Self::Md5 | Self::Sha1 => 12, Self::Sha224 => 16, Self::Sha256 => 24, Self::Sha384 => 32, Self::Sha512 => 48, }
151 }
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum PrivProtocol {
157 Des,
162 Des3,
167 Aes128,
169 Aes192,
171 Aes256,
173}
174
175impl std::fmt::Display for PrivProtocol {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 match self {
178 Self::Des => write!(f, "DES"),
179 Self::Des3 => write!(f, "3DES"),
180 Self::Aes128 => write!(f, "AES"),
181 Self::Aes192 => write!(f, "AES-192"),
182 Self::Aes256 => write!(f, "AES-256"),
183 }
184 }
185}
186
187impl std::str::FromStr for PrivProtocol {
188 type Err = ParseProtocolError;
189
190 fn from_str(s: &str) -> Result<Self, Self::Err> {
191 match s.to_ascii_uppercase().as_str() {
192 "DES" => Ok(Self::Des),
193 "3DES" | "3DES-EDE" | "DES3" | "TDES" => Ok(Self::Des3),
194 "AES" | "AES128" | "AES-128" => Ok(Self::Aes128),
195 "AES192" | "AES-192" => Ok(Self::Aes192),
196 "AES256" | "AES-256" => Ok(Self::Aes256),
197 _ => Err(ParseProtocolError {
198 input: s.to_string(),
199 kind: ProtocolKind::Priv,
200 }),
201 }
202 }
203}
204
205impl PrivProtocol {
206 pub fn key_len(self) -> usize {
208 match self {
209 Self::Des => 16, Self::Des3 => 32, Self::Aes128 => 16,
212 Self::Aes192 => 24,
213 Self::Aes256 => 32,
214 }
215 }
216
217 pub fn salt_len(self) -> usize {
219 8 }
221
222 pub(crate) fn key_extension_for(self, auth_protocol: AuthProtocol) -> KeyExtension {
231 let auth_len = auth_protocol.digest_len();
232 let priv_len = self.key_len();
233
234 if auth_len >= priv_len {
235 return KeyExtension::None;
236 }
237
238 match self {
239 Self::Des3 => KeyExtension::Reeder,
240 Self::Aes192 | Self::Aes256 => KeyExtension::Blumenthal,
241 Self::Des | Self::Aes128 => KeyExtension::None, }
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn test_auth_protocol_display() {
252 assert_eq!(format!("{}", AuthProtocol::Md5), "MD5");
253 assert_eq!(format!("{}", AuthProtocol::Sha1), "SHA");
254 assert_eq!(format!("{}", AuthProtocol::Sha224), "SHA-224");
255 assert_eq!(format!("{}", AuthProtocol::Sha256), "SHA-256");
256 assert_eq!(format!("{}", AuthProtocol::Sha384), "SHA-384");
257 assert_eq!(format!("{}", AuthProtocol::Sha512), "SHA-512");
258 }
259
260 #[test]
261 fn test_auth_protocol_from_str() {
262 assert_eq!("MD5".parse::<AuthProtocol>().unwrap(), AuthProtocol::Md5);
263 assert_eq!("md5".parse::<AuthProtocol>().unwrap(), AuthProtocol::Md5);
264 assert_eq!("SHA".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
265 assert_eq!("sha1".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
266 assert_eq!("SHA-1".parse::<AuthProtocol>().unwrap(), AuthProtocol::Sha1);
267 assert_eq!(
268 "sha-224".parse::<AuthProtocol>().unwrap(),
269 AuthProtocol::Sha224
270 );
271 assert_eq!(
272 "SHA256".parse::<AuthProtocol>().unwrap(),
273 AuthProtocol::Sha256
274 );
275 assert_eq!(
276 "SHA-256".parse::<AuthProtocol>().unwrap(),
277 AuthProtocol::Sha256
278 );
279 assert_eq!(
280 "sha384".parse::<AuthProtocol>().unwrap(),
281 AuthProtocol::Sha384
282 );
283 assert_eq!(
284 "SHA-512".parse::<AuthProtocol>().unwrap(),
285 AuthProtocol::Sha512
286 );
287
288 assert!("invalid".parse::<AuthProtocol>().is_err());
289 }
290
291 #[test]
292 fn test_priv_protocol_display() {
293 assert_eq!(format!("{}", PrivProtocol::Des), "DES");
294 assert_eq!(format!("{}", PrivProtocol::Des3), "3DES");
295 assert_eq!(format!("{}", PrivProtocol::Aes128), "AES");
296 assert_eq!(format!("{}", PrivProtocol::Aes192), "AES-192");
297 assert_eq!(format!("{}", PrivProtocol::Aes256), "AES-256");
298 }
299
300 #[test]
301 fn test_priv_protocol_from_str() {
302 assert_eq!("DES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des);
303 assert_eq!("des".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des);
304 assert_eq!("3DES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des3);
305 assert_eq!("3des".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des3);
306 assert_eq!(
307 "3DES-EDE".parse::<PrivProtocol>().unwrap(),
308 PrivProtocol::Des3
309 );
310 assert_eq!("DES3".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des3);
311 assert_eq!("TDES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Des3);
312 assert_eq!("AES".parse::<PrivProtocol>().unwrap(), PrivProtocol::Aes128);
313 assert_eq!("aes".parse::<PrivProtocol>().unwrap(), PrivProtocol::Aes128);
314 assert_eq!(
315 "AES128".parse::<PrivProtocol>().unwrap(),
316 PrivProtocol::Aes128
317 );
318 assert_eq!(
319 "AES-128".parse::<PrivProtocol>().unwrap(),
320 PrivProtocol::Aes128
321 );
322 assert_eq!(
323 "aes192".parse::<PrivProtocol>().unwrap(),
324 PrivProtocol::Aes192
325 );
326 assert_eq!(
327 "AES-192".parse::<PrivProtocol>().unwrap(),
328 PrivProtocol::Aes192
329 );
330 assert_eq!(
331 "aes256".parse::<PrivProtocol>().unwrap(),
332 PrivProtocol::Aes256
333 );
334 assert_eq!(
335 "AES-256".parse::<PrivProtocol>().unwrap(),
336 PrivProtocol::Aes256
337 );
338
339 assert!("invalid".parse::<PrivProtocol>().is_err());
340 }
341
342 #[test]
343 fn test_parse_protocol_error_display() {
344 let err = "bogus".parse::<AuthProtocol>().unwrap_err();
345 assert!(err.to_string().contains("bogus"));
346 assert!(err.to_string().contains("authentication protocol"));
347
348 let err = "bogus".parse::<PrivProtocol>().unwrap_err();
349 assert!(err.to_string().contains("bogus"));
350 assert!(err.to_string().contains("privacy protocol"));
351 }
352}