use std::{str::FromStr, string::ToString};
use ring::hmac;
use serde::{Deserialize, Serialize, de::Deserializer, ser::Serializer};
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
#[repr(u32)]
pub enum Method {
#[default]
TOTP = 0,
HOTP = 1,
Steam = 2,
Motp = 3,
Yandex = 4,
}
impl Serialize for Method {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Method {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::from_str(&String::deserialize(deserializer)?).unwrap())
}
}
impl From<u32> for Method {
fn from(u: u32) -> Self {
match u {
1 => Self::HOTP,
2 => Self::Steam,
3 => Self::Motp,
4 => Self::Yandex,
_ => Self::default(),
}
}
}
impl Method {
pub fn is_time_based(self) -> bool {
matches!(self, Self::TOTP | Self::Steam)
}
pub fn is_event_based(self) -> bool {
matches!(self, Self::HOTP)
}
pub fn to_string(self) -> String {
match self {
Self::HOTP => "Counter-based".to_string(),
Self::TOTP => "Time-based".to_string(),
Self::Steam => "Steam".to_string(),
Self::Motp => "MOTP".to_string(),
Self::Yandex => "Yandex".to_string(),
}
}
}
impl FromStr for Method {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_ref() {
"totp" | "otp" => Ok(Self::TOTP),
"hotp" => Ok(Self::HOTP),
"steam" => Ok(Self::Steam),
"motp" => Ok(Self::Motp),
"yandex" => Ok(Self::Yandex),
_ => anyhow::bail!("Unsupported Method"),
}
}
}
impl ToString for Method {
fn to_string(&self) -> String {
match *self {
Self::TOTP => "totp",
Self::HOTP => "hotp",
Self::Steam => "steam",
Self::Motp => "motp",
Self::Yandex => "yandex",
}
.to_string()
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
#[repr(u32)]
pub enum Algorithm {
#[default]
SHA1 = 0,
SHA256 = 1,
SHA512 = 2,
}
impl Serialize for Algorithm {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Algorithm {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self::from_str(&String::deserialize(deserializer)?).unwrap())
}
}
impl Algorithm {
pub fn to_string(self) -> String {
match self {
Self::SHA1 => "SHA1".to_string(),
Self::SHA256 => "SHA256".to_string(),
Self::SHA512 => "SHA512".to_string(),
}
}
}
impl FromStr for Algorithm {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_ref() {
"SHA1" => Ok(Self::SHA1),
"SHA256" => Ok(Self::SHA256),
"SHA512" => Ok(Self::SHA512),
_ => anyhow::bail!("Unsupported HMAC-algorithm"),
}
}
}
impl ToString for Algorithm {
fn to_string(&self) -> String {
match *self {
Self::SHA1 => "SHA1",
Self::SHA256 => "SHA256",
Self::SHA512 => "SHA512",
}
.to_string()
}
}
impl From<Algorithm> for hmac::Algorithm {
fn from(h: Algorithm) -> Self {
match h {
Algorithm::SHA1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
Algorithm::SHA256 => hmac::HMAC_SHA256,
Algorithm::SHA512 => hmac::HMAC_SHA512,
}
}
}
impl From<u32> for Algorithm {
fn from(u: u32) -> Self {
match u {
1 => Self::SHA256,
2 => Self::SHA512,
_ => Self::default(),
}
}
}