1use std::{str::FromStr, string::ToString};
2
3use ring::hmac;
4use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
5
6#[allow(clippy::upper_case_acronyms)]
7#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
8#[repr(u32)]
9pub enum Method {
11 #[default]
13 TOTP = 0,
14 HOTP = 1,
16 Steam = 2,
17}
18
19impl Serialize for Method {
20 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
21 where
22 S: Serializer,
23 {
24 serializer.serialize_str(&self.to_string())
25 }
26}
27
28impl<'de> Deserialize<'de> for Method {
29 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
30 where
31 D: Deserializer<'de>,
32 {
33 Ok(Self::from_str(&String::deserialize(deserializer)?).unwrap())
34 }
35}
36
37impl From<u32> for Method {
38 fn from(u: u32) -> Self {
39 match u {
40 1 => Self::HOTP,
41 2 => Self::Steam,
42 _ => Self::default(),
43 }
44 }
45}
46
47impl Method {
48 pub fn is_time_based(self) -> bool {
49 matches!(self, Self::TOTP | Self::Steam)
50 }
51
52 pub fn is_event_based(self) -> bool {
53 matches!(self, Self::HOTP)
54 }
55
56 pub fn to_string(self) -> String {
57 match self {
58 Self::HOTP => "Counter-based".to_string(),
59 Self::TOTP => "Time-based".to_string(),
60 Self::Steam => "Steam".to_string(),
61 }
62 }
63}
64
65impl FromStr for Method {
66 type Err = anyhow::Error;
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 match s.to_lowercase().as_ref() {
69 "totp" | "otp" => Ok(Self::TOTP),
70 "hotp" => Ok(Self::HOTP),
71 "steam" => Ok(Self::Steam),
72 _ => anyhow::bail!("Unsupported Method"),
73 }
74 }
75}
76
77impl ToString for Method {
78 fn to_string(&self) -> String {
79 match *self {
80 Self::TOTP => "totp",
81 Self::HOTP => "hotp",
82 Self::Steam => "steam",
83 }
84 .to_string()
85 }
86}
87
88#[allow(clippy::upper_case_acronyms)]
89#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
90#[repr(u32)]
91pub enum Algorithm {
93 #[default]
95 SHA1 = 0,
96 SHA256 = 1,
98 SHA512 = 2,
100}
101
102impl Serialize for Algorithm {
103 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104 where
105 S: Serializer,
106 {
107 serializer.serialize_str(&self.to_string())
108 }
109}
110
111impl<'de> Deserialize<'de> for Algorithm {
112 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
113 where
114 D: Deserializer<'de>,
115 {
116 Ok(Self::from_str(&String::deserialize(deserializer)?).unwrap())
117 }
118}
119
120impl Algorithm {
121 pub fn to_string(self) -> String {
122 match self {
123 Self::SHA1 => "SHA-1".to_string(),
124 Self::SHA256 => "SHA-256".to_string(),
125 Self::SHA512 => "SHA-512".to_string(),
126 }
127 }
128}
129
130impl FromStr for Algorithm {
131 type Err = anyhow::Error;
132 fn from_str(s: &str) -> Result<Self, Self::Err> {
133 match s.to_lowercase().as_ref() {
134 "sha1" => Ok(Self::SHA1),
135 "sha256" => Ok(Self::SHA256),
136 "sha512" => Ok(Self::SHA512),
137 _ => anyhow::bail!("Unsupported HMAC-algorithm"),
138 }
139 }
140}
141
142impl ToString for Algorithm {
143 fn to_string(&self) -> String {
144 match *self {
145 Self::SHA1 => "sha1",
146 Self::SHA256 => "sha256",
147 Self::SHA512 => "sha512",
148 }
149 .to_string()
150 }
151}
152
153impl From<Algorithm> for hmac::Algorithm {
154 fn from(h: Algorithm) -> Self {
155 match h {
156 Algorithm::SHA1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
157 Algorithm::SHA256 => hmac::HMAC_SHA256,
158 Algorithm::SHA512 => hmac::HMAC_SHA512,
159 }
160 }
161}
162
163impl From<u32> for Algorithm {
164 fn from(u: u32) -> Self {
165 match u {
166 1 => Self::SHA256,
167 2 => Self::SHA512,
168 _ => Self::default(),
169 }
170 }
171}