1use crate::cvt;
2use crate::error::ErrorStack;
3use crate::foreign_types::ForeignTypeRef;
4use crate::hash::MessageDigest;
5use openssl_macros::corresponds;
6
7foreign_type_and_impl_send_sync! {
8 type CType = ffi::HMAC_CTX;
9 fn drop = ffi::HMAC_CTX_free;
10
11 pub struct HmacCtx;
12}
13
14impl HmacCtxRef {
15 #[corresponds(HMAC_Init_ex)]
18 pub fn init(&mut self, key: &[u8], md: &MessageDigest) -> Result<(), ErrorStack> {
19 ffi::init();
20
21 unsafe {
22 cvt(ffi::HMAC_Init_ex(
23 self.as_ptr(),
24 key.as_ptr().cast(),
25 key.len(),
26 md.as_ptr(),
27 core::ptr::null_mut(),
29 ))
30 }
31 }
32}
33
34pub struct Hmac(*mut ffi::HMAC_CTX);
36
37impl Hmac {
38 pub fn init(key: &[u8], md: &MessageDigest) -> Result<Hmac, ErrorStack> {
40 ffi::init();
41
42 let ctx = unsafe {
43 let ctx = ffi::HMAC_CTX_new();
44 cvt(ffi::HMAC_Init_ex(
45 ctx,
46 key.as_ptr().cast(),
47 key.len(),
48 md.as_ptr(),
49 core::ptr::null_mut(),
51 ))?;
52 ctx
53 };
54
55 Ok(Hmac(ctx))
56 }
57
58 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
60 unsafe { cvt(ffi::HMAC_Update(self.0, data.as_ptr().cast(), data.len())) }
61 }
62
63 pub fn finalize(self) -> Result<Vec<u8>, ErrorStack> {
65 let out_len = unsafe { ffi::HMAC_size(self.0) };
66 let mut out = vec![0; out_len];
67 unsafe {
68 cvt(ffi::HMAC_Final(
69 self.0,
70 out.as_mut_ptr().cast(),
71 core::ptr::null_mut(),
73 ))?;
74 }
75 Ok(out)
76 }
77}
78
79impl Drop for Hmac {
80 fn drop(&mut self) {
81 unsafe { ffi::HMAC_CTX_free(self.0) }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use crate::hash;
88
89 use super::*;
90
91 fn test<const N: usize>(md: MessageDigest) {
92 assert_eq!(N, md.size());
93 let key = vec![0; N];
94 let message_parts = [
95 b"hello".to_vec(),
96 b"world!".to_vec(),
97 b"".to_vec(),
98 vec![0; 23],
99 b"fella guy".to_vec(),
100 ];
101 let message = message_parts.concat();
102
103 let mut hmac = Hmac::init(&key, &md).unwrap();
104 for part in &message_parts {
105 hmac.update(part).unwrap();
106 }
107 let res = hmac.finalize().unwrap();
108 assert_eq!(res, hash::hmac::<N>(md, &key, &message).unwrap());
109 }
110
111 #[test]
112 fn test_sha1() {
113 test::<20>(MessageDigest::sha1());
114 }
115
116 #[test]
117 fn test_sha256() {
118 test::<32>(MessageDigest::sha256());
119 }
120
121 #[test]
122 fn test_sha384() {
123 test::<48>(MessageDigest::sha384());
124 }
125
126 #[test]
127 fn test_sha512() {
128 test::<64>(MessageDigest::sha512());
129 }
130}