otp_std/
algorithm.rs

1//! Hash-based Message Authentication Code (HMAC) functionality.
2
3use 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
21/// HMAC type using SHA-1.
22pub type HmacSha1 = Hmac<Sha1>;
23
24/// HMAC type using SHA-256.
25#[cfg(feature = "sha2")]
26pub type HmacSha256 = Hmac<Sha256>;
27
28/// HMAC type using SHA-512.
29#[cfg(feature = "sha2")]
30pub type HmacSha512 = Hmac<Sha512>;
31
32/// Represents errors that occur when unknown algorithms are encountered.
33#[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    /// The unknown algorithm.
38    pub unknown: String,
39}
40
41impl Error {
42    /// Constructs [`Self`].
43    pub const fn new(unknown: String) -> Self {
44        Self { unknown }
45    }
46}
47
48/// Represents hash algorithms used in HMACs.
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
50pub enum Algorithm {
51    /// SHA-1 algorithm.
52    #[default]
53    Sha1,
54    /// SHA-256 algorithm.
55    #[cfg(feature = "sha2")]
56    Sha256,
57    /// SHA-512 algorithm.
58    #[cfg(feature = "sha2")]
59    Sha512,
60}
61
62impl Algorithm {
63    /// The amount of algorithms available.
64    #[cfg(not(feature = "sha2"))]
65    pub const COUNT: usize = 1;
66
67    /// The array of algorithms available.
68    #[cfg(not(feature = "sha2"))]
69    pub const ARRAY: [Self; Self::COUNT] = [Self::Sha1];
70
71    /// The amount of algorithms available.
72    #[cfg(feature = "sha2")]
73    pub const COUNT: usize = 3;
74
75    /// The array of algorithms available.
76    #[cfg(feature = "sha2")]
77    pub const ARRAY: [Self; Self::COUNT] = [Self::Sha1, Self::Sha256, Self::Sha512];
78}
79
80/// The `SHA1` literal.
81pub const SHA1: &str = "SHA1";
82
83/// The length of the SHA-1 hash.
84pub const SHA1_LENGTH: usize = 20;
85
86/// The length of the SHA-256 hash.
87#[cfg(feature = "sha2")]
88pub const SHA256_LENGTH: usize = 32;
89
90/// The length of the SHA-512 hash.
91#[cfg(feature = "sha2")]
92pub const SHA512_LENGTH: usize = 64;
93
94/// The `SHA256` literal.
95#[cfg(feature = "sha2")]
96pub const SHA256: &str = "SHA256";
97
98/// The `SHA512` literal.
99#[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    /// Returns the static string representation of [`Self`].
120    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    /// Returns the recommended length of the key for [`Self`].
131    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    /// Computes HMAC using the [`Self`] algorithm, the key provided, and the given data.
142    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
180/// Computes the HMAC of the given data.
181pub 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
187/// HMAC accepts any key length.
188pub const HMAC_ANY_KEY_LENGTH: &str = "hmac accepts any key length";
189
190/// Creates HMAC using the SHA-1 algorithm.
191///
192/// # Panics
193///
194/// HMAC accepts any key length, which means this function will not panic.
195pub 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
199/// Computes the HMAC using the SHA-1 algorithm.
200pub 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/// Creates HMAC using the SHA-256 algorithm.
205///
206/// # Panics
207///
208/// HMAC accepts any key length, which means this function will not panic.
209#[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/// Computes the HMAC using the SHA-256 algorithm.
215#[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/// Creates HMAC using the SHA-512 algorithm.
221///
222/// # Panics
223///
224/// HMAC accepts any key length, which means this function will not panic.
225#[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/// Computes the HMAC using the SHA-512 algorithm.
231#[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}