1use crate::error::ErrorStack;
7use native_ossl_sys as sys;
8use std::ffi::CStr;
9use std::sync::Arc;
10
11pub struct MacAlg {
17 ptr: *mut sys::EVP_MAC,
18 lib_ctx: Option<Arc<crate::lib_ctx::LibCtx>>,
20}
21
22impl MacAlg {
23 pub fn fetch(name: &CStr, props: Option<&CStr>) -> Result<Self, ErrorStack> {
29 let props_ptr = props.map_or(std::ptr::null(), CStr::as_ptr);
30 let ptr = unsafe {
31 sys::EVP_MAC_fetch(std::ptr::null_mut(), name.as_ptr(), props_ptr)
32 };
33 if ptr.is_null() {
34 return Err(ErrorStack::drain());
35 }
36 Ok(MacAlg { ptr, lib_ctx: None })
37 }
38
39 pub fn fetch_in(
43 ctx: &Arc<crate::lib_ctx::LibCtx>,
44 name: &CStr,
45 props: Option<&CStr>,
46 ) -> Result<Self, ErrorStack> {
47 let props_ptr = props.map_or(std::ptr::null(), CStr::as_ptr);
48 let ptr = unsafe {
49 sys::EVP_MAC_fetch(ctx.as_ptr(), name.as_ptr(), props_ptr)
50 };
51 if ptr.is_null() {
52 return Err(ErrorStack::drain());
53 }
54 Ok(MacAlg {
55 ptr,
56 lib_ctx: Some(Arc::clone(ctx)),
57 })
58 }
59
60 #[must_use]
62 pub fn as_ptr(&self) -> *const sys::EVP_MAC {
63 self.ptr
64 }
65
66 #[must_use]
70 pub fn name(&self) -> &CStr {
71 unsafe { CStr::from_ptr(sys::EVP_MAC_get0_name(self.ptr)) }
75 }
76}
77
78impl Clone for MacAlg {
79 fn clone(&self) -> Self {
80 unsafe { sys::EVP_MAC_up_ref(self.ptr) };
81 MacAlg {
82 ptr: self.ptr,
83 lib_ctx: self.lib_ctx.clone(),
84 }
85 }
86}
87
88impl Drop for MacAlg {
89 fn drop(&mut self) {
90 unsafe { sys::EVP_MAC_free(self.ptr) };
91 }
92}
93
94unsafe impl Send for MacAlg {}
96unsafe impl Sync for MacAlg {}
97
98pub struct MacCtx {
105 ptr: *mut sys::EVP_MAC_CTX,
106}
107
108impl MacCtx {
109 pub fn new(alg: &MacAlg) -> Result<Self, ErrorStack> {
115 let ptr = unsafe { sys::EVP_MAC_CTX_new(alg.ptr) };
116 if ptr.is_null() {
117 return Err(ErrorStack::drain());
118 }
119 Ok(MacCtx { ptr })
120 }
121
122 pub fn init(
129 &mut self,
130 key: &[u8],
131 params: Option<&crate::params::Params<'_>>,
132 ) -> Result<(), ErrorStack> {
133 let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
134 crate::ossl_call!(sys::EVP_MAC_init(
135 self.ptr,
136 key.as_ptr(),
137 key.len(),
138 params_ptr
139 ))
140 }
141
142 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
146 crate::ossl_call!(sys::EVP_MAC_update(self.ptr, data.as_ptr(), data.len()))
147 }
148
149 pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
156 let mut outl: usize = 0;
157 crate::ossl_call!(sys::EVP_MAC_final(
158 self.ptr,
159 out.as_mut_ptr(),
160 std::ptr::addr_of_mut!(outl),
161 out.len()
162 ))?;
163 Ok(outl)
164 }
165
166 pub fn finish_xof(&mut self, out: &mut [u8]) -> Result<(), ErrorStack> {
170 crate::ossl_call!(sys::EVP_MAC_finalXOF(self.ptr, out.as_mut_ptr(), out.len()))
171 }
172
173 #[must_use]
175 pub fn mac_size(&self) -> usize {
176 unsafe { sys::EVP_MAC_CTX_get_mac_size(self.ptr) }
177 }
178
179 pub fn fork(&self) -> Result<MacCtx, ErrorStack> {
185 let ptr = unsafe { sys::EVP_MAC_CTX_dup(self.ptr) };
186 if ptr.is_null() {
187 return Err(ErrorStack::drain());
188 }
189 Ok(MacCtx { ptr })
190 }
191}
192
193impl Drop for MacCtx {
194 fn drop(&mut self) {
195 unsafe { sys::EVP_MAC_CTX_free(self.ptr) };
196 }
197}
198
199unsafe impl Send for MacCtx {}
200
201pub struct HmacCtx(MacCtx);
205
206impl HmacCtx {
207 pub fn new(
213 digest: &crate::digest::DigestAlg,
214 key: &[u8],
215 ) -> Result<Self, ErrorStack> {
216 let alg = MacAlg::fetch(c"HMAC", None)?;
218 let mut ctx = MacCtx::new(&alg)?;
219
220 let nid = digest.nid();
223 let name_cstr: std::ffi::CString = {
224 let name_ptr = unsafe {
225 native_ossl_sys::OBJ_nid2sn(nid)
226 };
227 if name_ptr.is_null() {
228 return Err(crate::error::ErrorStack::drain());
229 }
230 unsafe { std::ffi::CStr::from_ptr(name_ptr) }.to_owned()
231 };
232
233 let params = crate::params::ParamBuilder::new()?
234 .push_utf8_string(c"digest", &name_cstr)?
235 .build()?;
236
237 ctx.init(key, Some(¶ms))?;
238 Ok(HmacCtx(ctx))
239 }
240
241 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
245 self.0.update(data)
246 }
247
248 pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
252 self.0.finish(out)
253 }
254
255 pub fn finish_to_vec(&mut self) -> Result<Vec<u8>, ErrorStack> {
259 let size = self.0.mac_size();
260 let mut out = vec![0u8; size];
261 let n = self.0.finish(&mut out)?;
262 out.truncate(n);
263 Ok(out)
264 }
265
266 #[must_use]
268 pub fn mac_size(&self) -> usize {
269 self.0.mac_size()
270 }
271
272 pub fn oneshot(
276 digest: &crate::digest::DigestAlg,
277 key: &[u8],
278 data: &[u8],
279 out: &mut [u8],
280 ) -> Result<usize, ErrorStack> {
281 let mut ctx = HmacCtx::new(digest, key)?;
282 ctx.update(data)?;
283 ctx.finish(out)
284 }
285}
286
287pub struct CmacCtx(MacCtx);
291
292impl CmacCtx {
293 pub fn new(
297 cipher: &crate::cipher::CipherAlg,
298 key: &[u8],
299 ) -> Result<Self, ErrorStack> {
300 let alg = MacAlg::fetch(c"CMAC", None)?;
301 let mut ctx = MacCtx::new(&alg)?;
302
303 let key_len = cipher.key_len();
312 let cipher_name: &std::ffi::CStr = match key_len {
317 16 => c"AES-128-CBC",
318 24 => c"AES-192-CBC",
319 32 => c"AES-256-CBC",
320 _ => return Err(ErrorStack::drain()),
321 };
322
323 let params = crate::params::ParamBuilder::new()?
324 .push_utf8_string(c"cipher", cipher_name)?
325 .build()?;
326
327 ctx.init(key, Some(¶ms))?;
328 Ok(CmacCtx(ctx))
329 }
330
331 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
335 self.0.update(data)
336 }
337
338 pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
342 self.0.finish(out)
343 }
344}
345
346#[cfg(test)]
349mod tests {
350 use super::*;
351 use crate::digest::DigestAlg;
352
353 #[test]
354 fn fetch_hmac_succeeds() {
355 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
356 drop(alg);
357 }
358
359 #[test]
360 fn fetch_nonexistent_fails() {
361 assert!(MacAlg::fetch(c"NONEXISTENT_MAC_XYZ", None).is_err());
362 }
363
364 #[test]
365 fn clone_then_drop_both() {
366 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
367 let alg2 = alg.clone();
368 drop(alg);
369 drop(alg2);
370 }
371
372 #[test]
377 fn hmac_sha256_rfc4231_tv1() {
378 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
379 let key = [0x0b_u8; 20];
380 let data = b"Hi There";
381
382 let mut ctx = HmacCtx::new(&digest, &key).unwrap();
383 ctx.update(data).unwrap();
384 let mac = ctx.finish_to_vec().unwrap();
385
386 assert_eq!(
387 hex::encode(&mac),
388 "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
389 );
390 }
391
392 #[test]
394 fn hmac_sha256_oneshot() {
395 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
396 let key = [0x0b_u8; 20];
397 let mut out = [0u8; 32];
398 let n = HmacCtx::oneshot(&digest, &key, b"Hi There", &mut out).unwrap();
399 assert_eq!(n, 32);
400 assert_eq!(
401 hex::encode(out),
402 "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
403 );
404 }
405
406 #[test]
408 fn mac_ctx_fork_mid_stream() {
409 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
410 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
411 let nid = digest.nid();
412 let name_ptr = unsafe { native_ossl_sys::OBJ_nid2sn(nid) };
413 let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
414 let params = crate::params::ParamBuilder::new().unwrap()
415 .push_utf8_string(c"digest", name).unwrap()
416 .build().unwrap();
417
418 let mut ctx = MacCtx::new(&alg).unwrap();
419 ctx.init(&[0u8; 32], Some(¶ms)).unwrap();
420 ctx.update(b"common").unwrap();
421
422 let mut fork = ctx.fork().unwrap();
423 ctx.update(b" A").unwrap();
424 fork.update(b" B").unwrap();
425
426 let mut out_a = [0u8; 32];
427 let mut out_b = [0u8; 32];
428 ctx.finish(&mut out_a).unwrap();
429 fork.finish(&mut out_b).unwrap();
430
431 assert_ne!(out_a, out_b);
432 }
433}