1use std::ffi::c_void;
2
3#[repr(u32)]
4enum Algorithm {
5 SHA1,
6 MD5,
7 SHA256,
8 SHA384,
9 SHA512,
10 SHA224,
11}
12
13#[repr(C)]
14struct Context {
15 ctx: [u32; 96],
16}
17
18impl Default for Context {
19 fn default() -> Self {
20 Self { ctx: [0u32; 96] }
21 }
22}
23
24extern "C" {
25 fn CCHmac(
26 algorithm: Algorithm,
27 key: *const c_void,
28 key_len: usize,
29 data: *const c_void,
30 data_len: usize,
31 out: *mut c_void,
32 );
33
34 fn CCHmacInit(ctx: *mut Context, algorithm: Algorithm, key: *const c_void, len: usize);
35
36 fn CCHmacUpdate(ctx: *mut Context, data: *const c_void, len: usize);
37
38 fn CCHmacFinal(ctx: *mut Context, output: *mut c_void);
39}
40
41pub struct HMAC;
42
43impl HMAC {
44 fn generate(algorithm: Algorithm, key: &[u8], data: &[u8], hash: &mut [u8]) {
45 unsafe {
46 CCHmac(
47 algorithm,
48 key.as_ptr() as *const c_void,
49 key.len(),
50 data.as_ptr() as *const c_void,
51 data.len(),
52 hash.as_mut_ptr() as *mut c_void,
53 )
54 }
55 }
56}
57
58macro_rules! implement_digest {
59 ($func:ident, $algorithm:ident, $len:expr) => {
60 impl HMAC {
61 pub fn $func(key: impl AsRef<[u8]>, data: impl AsRef<[u8]>) -> [u8; $len] {
62 let mut hash = [0u8; $len];
63
64 Self::generate(
65 Algorithm::$algorithm,
66 key.as_ref(),
67 data.as_ref(),
68 &mut hash,
69 );
70
71 hash
72 }
73 }
74
75 pub struct $algorithm {
76 context: Context,
77 }
78
79 impl $algorithm {
80 pub fn new(key: impl AsRef<[u8]>) -> Self {
81 let mut context = Context::default();
82
83 unsafe {
84 CCHmacInit(
85 &mut context,
86 Algorithm::$algorithm,
87 key.as_ref().as_ptr() as *const c_void,
88 key.as_ref().len(),
89 );
90 }
91
92 Self { context }
93 }
94
95 pub fn update(&mut self, data: impl AsRef<[u8]>) {
96 unsafe {
97 CCHmacUpdate(
98 &mut self.context,
99 data.as_ref().as_ptr() as *const c_void,
100 data.as_ref().len(),
101 );
102 }
103 }
104
105 pub fn finish(mut self) -> [u8; $len] {
106 let mut output = [0u8; $len];
107
108 unsafe {
109 CCHmacFinal(&mut self.context, output.as_mut_ptr() as *mut c_void);
110 }
111
112 output
113 }
114 }
115 };
116}
117
118implement_digest!(md5, MD5, 16);
119implement_digest!(sha1, SHA1, 20);
120implement_digest!(sha224, SHA224, 28);
121implement_digest!(sha256, SHA256, 32);
122implement_digest!(sha384, SHA384, 48);
123implement_digest!(sha512, SHA512, 64);