1use std::{fmt, str::FromStr};
4
5use hmac::{Hmac, Mac};
6
7use miette::Diagnostic;
8
9#[cfg(feature = "serde")]
10use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
11
12use sha1::Sha1;
13
14#[cfg(feature = "sha2")]
15use sha2::{Sha256, Sha512};
16
17use thiserror::Error;
18
19use crate::macros::errors;
20
21pub type HmacSha1 = Hmac<Sha1>;
23
24#[cfg(feature = "sha2")]
26pub type HmacSha256 = Hmac<Sha256>;
27
28#[cfg(feature = "sha2")]
30pub type HmacSha512 = Hmac<Sha512>;
31
32#[derive(Debug, Error, Diagnostic)]
34#[error("unknown algorithm `{unknown}`")]
35#[diagnostic(code(otp_std::algorithm), help("make sure the algorithm is supported"))]
36pub struct Error {
37 pub unknown: String,
39}
40
41impl Error {
42 pub const fn new(unknown: String) -> Self {
44 Self { unknown }
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
50pub enum Algorithm {
51 #[default]
53 Sha1,
54 #[cfg(feature = "sha2")]
56 Sha256,
57 #[cfg(feature = "sha2")]
59 Sha512,
60}
61
62impl Algorithm {
63 #[cfg(not(feature = "sha2"))]
65 pub const COUNT: usize = 1;
66
67 #[cfg(not(feature = "sha2"))]
69 pub const ARRAY: [Self; Self::COUNT] = [Self::Sha1];
70
71 #[cfg(feature = "sha2")]
73 pub const COUNT: usize = 3;
74
75 #[cfg(feature = "sha2")]
77 pub const ARRAY: [Self; Self::COUNT] = [Self::Sha1, Self::Sha256, Self::Sha512];
78}
79
80pub const SHA1: &str = "SHA1";
82
83pub const SHA1_LENGTH: usize = 20;
85
86#[cfg(feature = "sha2")]
88pub const SHA256_LENGTH: usize = 32;
89
90#[cfg(feature = "sha2")]
92pub const SHA512_LENGTH: usize = 64;
93
94#[cfg(feature = "sha2")]
96pub const SHA256: &str = "SHA256";
97
98#[cfg(feature = "sha2")]
100pub const SHA512: &str = "SHA512";
101
102#[cfg(feature = "serde")]
103impl Serialize for Algorithm {
104 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
105 self.static_str().serialize(serializer)
106 }
107}
108
109#[cfg(feature = "serde")]
110impl<'de> Deserialize<'de> for Algorithm {
111 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
112 let string = <&str>::deserialize(deserializer)?;
113
114 string.parse().map_err(de::Error::custom)
115 }
116}
117
118impl Algorithm {
119 pub const fn static_str(self) -> &'static str {
121 match self {
122 Self::Sha1 => SHA1,
123 #[cfg(feature = "sha2")]
124 Self::Sha256 => SHA256,
125 #[cfg(feature = "sha2")]
126 Self::Sha512 => SHA512,
127 }
128 }
129
130 pub const fn recommended_length(self) -> usize {
132 match self {
133 Self::Sha1 => SHA1_LENGTH,
134 #[cfg(feature = "sha2")]
135 Self::Sha256 => SHA256_LENGTH,
136 #[cfg(feature = "sha2")]
137 Self::Sha512 => SHA512_LENGTH,
138 }
139 }
140
141 pub fn hmac<K: AsRef<[u8]>, D: AsRef<[u8]>>(self, key: K, data: D) -> Vec<u8> {
143 match self {
144 Self::Sha1 => hmac_sha1(key, data),
145 #[cfg(feature = "sha2")]
146 Self::Sha256 => hmac_sha256(key, data),
147 #[cfg(feature = "sha2")]
148 Self::Sha512 => hmac_sha512(key, data),
149 }
150 }
151}
152
153errors! {
154 Type = Error,
155 Hack = $,
156 error => new(string => to_owned),
157}
158
159impl FromStr for Algorithm {
160 type Err = Error;
161
162 fn from_str(string: &str) -> Result<Self, Self::Err> {
163 match string {
164 SHA1 => Ok(Self::Sha1),
165 #[cfg(feature = "sha2")]
166 SHA256 => Ok(Self::Sha256),
167 #[cfg(feature = "sha2")]
168 SHA512 => Ok(Self::Sha512),
169 _ => Err(error!(string)),
170 }
171 }
172}
173
174impl fmt::Display for Algorithm {
175 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
176 self.static_str().fmt(formatter)
177 }
178}
179
180pub fn hmac<M: Mac, D: AsRef<[u8]>>(mut mac: M, data: D) -> Vec<u8> {
182 mac.update(data.as_ref());
183
184 mac.finalize().into_bytes().to_vec()
185}
186
187pub const HMAC_ANY_KEY_LENGTH: &str = "hmac accepts any key length";
189
190pub fn new_hmac_sha1<K: AsRef<[u8]>>(key: K) -> HmacSha1 {
196 HmacSha1::new_from_slice(key.as_ref()).expect(HMAC_ANY_KEY_LENGTH)
197}
198
199pub fn hmac_sha1<K: AsRef<[u8]>, D: AsRef<[u8]>>(key: K, data: D) -> Vec<u8> {
201 hmac(new_hmac_sha1(key), data)
202}
203
204#[cfg(feature = "sha2")]
210pub fn new_hmac_sha256<K: AsRef<[u8]>>(key: K) -> HmacSha256 {
211 HmacSha256::new_from_slice(key.as_ref()).expect(HMAC_ANY_KEY_LENGTH)
212}
213
214#[cfg(feature = "sha2")]
216pub fn hmac_sha256<K: AsRef<[u8]>, D: AsRef<[u8]>>(key: K, data: D) -> Vec<u8> {
217 hmac(new_hmac_sha256(key), data)
218}
219
220#[cfg(feature = "sha2")]
226pub fn new_hmac_sha512<K: AsRef<[u8]>>(key: K) -> HmacSha512 {
227 HmacSha512::new_from_slice(key.as_ref()).expect(HMAC_ANY_KEY_LENGTH)
228}
229
230#[cfg(feature = "sha2")]
232pub fn hmac_sha512<K: AsRef<[u8]>, D: AsRef<[u8]>>(key: K, data: D) -> Vec<u8> {
233 hmac(new_hmac_sha512(key), data)
234}