Skip to content

Commit e3eea9a

Browse files
feat(hmac): add implementation for hmac-sha512 (#74)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
1 parent e0a117f commit e3eea9a

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,8 @@ harness = false
9292
[[bench]]
9393
name = "encryption"
9494
harness = false
95+
96+
97+
[[bench]]
98+
name = "hashing"
99+
harness = false

benches/hashing.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#![allow(clippy::expect_used)]
2+
#![allow(clippy::missing_panics_doc)]
3+
4+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
5+
use tartarus::crypto::{sha::HmacSha512, Encode};
6+
7+
const ITERATION: u32 = 14;
8+
9+
criterion_main!(benches);
10+
criterion_group!(benches, criterion_hmac_sha512);
11+
12+
macro_rules! const_iter {
13+
($algo:ident, $body:block, $key:ident, $($ty:expr),*) => {
14+
$(
15+
let $algo = $ty($key.clone());
16+
$body
17+
)*
18+
};
19+
}
20+
21+
pub fn criterion_hmac_sha512(c: &mut Criterion) {
22+
let key: masking::Secret<_> = (0..1000)
23+
.map(|_| rand::random::<u8>())
24+
.collect::<Vec<_>>()
25+
.into();
26+
27+
const_iter!(
28+
algo,
29+
{
30+
let mut group = c.benchmark_group(format!("{}", algo));
31+
(1..ITERATION).for_each(|po| {
32+
let max: u64 = (2_u64).pow(po);
33+
let value = (0..max).map(|_| rand::random::<u8>()).collect::<Vec<_>>();
34+
let hashed = algo.encode(value.clone()).expect("Failed while hashing");
35+
group.throughput(criterion::Throughput::Bytes(max));
36+
group.bench_with_input(
37+
criterion::BenchmarkId::from_parameter(format!("{}", max)),
38+
&value,
39+
|b, value| {
40+
b.iter(|| {
41+
black_box(
42+
algo.encode(black_box(value.clone()))
43+
.expect("Failed while hashing")
44+
== hashed,
45+
)
46+
})
47+
},
48+
);
49+
})
50+
},
51+
key,
52+
HmacSha512::<1>::new,
53+
HmacSha512::<10>::new,
54+
HmacSha512::<100>::new,
55+
HmacSha512::<1000>::new
56+
);
57+
}

src/crypto/sha.rs

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use masking::PeekInterface;
2+
use ring::hmac;
3+
14
use crate::error;
25

36
///
@@ -13,3 +16,172 @@ impl super::Encode<Vec<u8>, Vec<u8>> for Sha512 {
1316
Ok(digest.as_ref().to_vec())
1417
}
1518
}
19+
20+
///
21+
/// Type providing encoding functional to perform HMAC-SHA512 hashing
22+
///
23+
/// # Example
24+
///
25+
///```
26+
/// use tartarus::crypto::sha::HmacSha512;
27+
/// use tartarus::crypto::Encode;
28+
///
29+
/// let data = "Hello, World!";
30+
/// let key = "key";
31+
/// let algo = HmacSha512::<1>::new(key.as_bytes().to_vec().into());
32+
/// let hash = algo.encode(data.as_bytes().to_vec()).unwrap();
33+
///
34+
/// ```
35+
///
36+
/// This will not compile if `N` is less than or equal to 0.
37+
///
38+
/// ```compile_fail
39+
///
40+
/// use tartarus::crypto::sha::HmacSha512;
41+
/// use tartarus::crypto::Encode;
42+
///
43+
/// let key = "key";
44+
/// let algo = HmacSha512::<0>::new(key.as_bytes().to_vec().into());
45+
///
46+
///
47+
/// ```
48+
///
49+
///
50+
pub struct HmacSha512<const N: usize = 1>(masking::Secret<Vec<u8>>);
51+
52+
impl<const N: usize> HmacSha512<N> {
53+
pub fn new(key: masking::Secret<Vec<u8>>) -> Self {
54+
#[allow(clippy::let_unit_value)]
55+
let _ = <Self as AssertGt0>::VALID;
56+
57+
Self(key)
58+
}
59+
}
60+
61+
impl<const N: usize> std::fmt::Display for HmacSha512<N> {
62+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63+
write!(f, "HmacSha512<{}>", N)
64+
}
65+
}
66+
67+
impl<const N: usize> super::Encode<Vec<u8>, Vec<u8>> for HmacSha512<N> {
68+
type ReturnType<T> = Result<T, error::ContainerError<error::CryptoError>>;
69+
70+
fn encode(&self, input: Vec<u8>) -> Self::ReturnType<Vec<u8>> {
71+
let key = hmac::Key::new(ring::hmac::HMAC_SHA512, self.0.peek());
72+
let first = hmac::sign(&key, &input);
73+
74+
let signature = (0..=(N - 1)).fold(first, |input, _| hmac::sign(&key, input.as_ref()));
75+
76+
Ok(signature.as_ref().to_vec())
77+
}
78+
}
79+
80+
trait AssertGt0 {
81+
const VALID: ();
82+
}
83+
84+
impl<const N: usize> AssertGt0 for HmacSha512<N> {
85+
const VALID: () = assert!(N > 0);
86+
}
87+
88+
#[cfg(test)]
89+
mod tests {
90+
#![allow(clippy::unwrap_used, clippy::expect_used)]
91+
//!
92+
//! Testing HMAC-SHA512 encoding consists of 3 variables.
93+
//! 1. The input data
94+
//! 2. The Key
95+
//! 3. The `N` value
96+
//!
97+
98+
use crate::crypto::Encode;
99+
100+
use super::*;
101+
102+
#[test]
103+
fn test_input_data_equal() {
104+
let data1 = "Hello, World!";
105+
let data2 = "Hello, World!";
106+
let key = "key";
107+
108+
let algo = HmacSha512::<1>::new(key.as_bytes().to_vec().into());
109+
110+
let hash1 = algo.encode(data1.as_bytes().to_vec()).unwrap();
111+
let hash2 = algo.encode(data2.as_bytes().to_vec()).unwrap();
112+
113+
assert_eq!(hash1, hash2);
114+
}
115+
116+
#[test]
117+
fn test_input_data_not_equal() {
118+
let data1 = "Hello, World!";
119+
let data2 = "Hello, world";
120+
let key = "key";
121+
122+
let algo = HmacSha512::<1>::new(key.as_bytes().to_vec().into());
123+
124+
let hash1 = algo.encode(data1.as_bytes().to_vec()).unwrap();
125+
let hash2 = algo.encode(data2.as_bytes().to_vec()).unwrap();
126+
127+
assert_ne!(hash1, hash2);
128+
}
129+
130+
#[test]
131+
fn test_key_not_equal() {
132+
let data = "Hello, World!";
133+
let key1 = "key1";
134+
let key2 = "key2";
135+
136+
let algo1 = HmacSha512::<1>::new(key1.as_bytes().to_vec().into());
137+
let algo2 = HmacSha512::<1>::new(key2.as_bytes().to_vec().into());
138+
139+
let hash1 = algo1.encode(data.as_bytes().to_vec()).unwrap();
140+
let hash2 = algo2.encode(data.as_bytes().to_vec()).unwrap();
141+
142+
assert_ne!(hash1, hash2);
143+
}
144+
145+
#[test]
146+
fn test_key_equal() {
147+
let data = "Hello, World!";
148+
let key1 = "key";
149+
let key2 = "key";
150+
151+
let algo1 = HmacSha512::<1>::new(key1.as_bytes().to_vec().into());
152+
let algo2 = HmacSha512::<1>::new(key2.as_bytes().to_vec().into());
153+
154+
let hash1 = algo1.encode(data.as_bytes().to_vec()).unwrap();
155+
let hash2 = algo2.encode(data.as_bytes().to_vec()).unwrap();
156+
157+
assert_eq!(hash1, hash2);
158+
}
159+
160+
#[test]
161+
fn test_n_equal() {
162+
let data = "Hello, World!";
163+
let key = "key";
164+
165+
let algo1 = HmacSha512::<10>::new(key.as_bytes().to_vec().into());
166+
let algo2 = HmacSha512::<10>::new(key.as_bytes().to_vec().into());
167+
168+
let hash1 = algo1.encode(data.as_bytes().to_vec()).unwrap();
169+
let hash2 = algo2.encode(data.as_bytes().to_vec()).unwrap();
170+
171+
assert_eq!(hash1, hash2);
172+
}
173+
174+
#[test]
175+
fn test_n_not_equal() {
176+
let data = "Hello, World!";
177+
let key = "key";
178+
179+
let algo1 = HmacSha512::<10>::new(key.as_bytes().to_vec().into());
180+
let algo2 = HmacSha512::<20>::new(key.as_bytes().to_vec().into());
181+
182+
let hash1 = algo1.encode(data.as_bytes().to_vec()).unwrap();
183+
let hash2 = algo2.encode(data.as_bytes().to_vec()).unwrap();
184+
185+
assert_ne!(hash1, hash2);
186+
}
187+
}

0 commit comments

Comments
 (0)