use core::fmt;
use core::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sha2::Digest as _;
use super::SdJwtError;
use crate::jose::base64url;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum HashAlgorithm {
#[default]
Sha256,
Sha384,
Sha512,
}
impl HashAlgorithm {
pub const CLAIM: &'static str = "_sd_alg";
pub const fn name(self) -> &'static str {
match self {
Self::Sha256 => "sha-256",
Self::Sha384 => "sha-384",
Self::Sha512 => "sha-512",
}
}
pub fn parse(name: &str) -> Result<Self, SdJwtError> {
match name {
"sha-256" => Ok(Self::Sha256),
"sha-384" => Ok(Self::Sha384),
"sha-512" => Ok(Self::Sha512),
_ => Err(SdJwtError::UnsupportedHashAlgorithm(name.to_owned())),
}
}
pub fn digest(self, data: &[u8]) -> String {
let digest = match self {
Self::Sha256 => sha2::Sha256::digest(data).to_vec(),
Self::Sha384 => sha2::Sha384::digest(data).to_vec(),
Self::Sha512 => sha2::Sha512::digest(data).to_vec(),
};
base64url::encode(&digest)
}
}
impl fmt::Display for HashAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
impl FromStr for HashAlgorithm {
type Err = SdJwtError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl Serialize for HashAlgorithm {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.name())
}
}
impl<'de> Deserialize<'de> for HashAlgorithm {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let name = String::deserialize(deserializer)?;
Self::parse(&name).map_err(serde::de::Error::custom)
}
}