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 { sys::EVP_MAC_fetch(std::ptr::null_mut(), name.as_ptr(), props_ptr) };
31 if ptr.is_null() {
32 return Err(ErrorStack::drain());
33 }
34 Ok(MacAlg { ptr, lib_ctx: None })
35 }
36
37 pub fn fetch_in(
41 ctx: &Arc<crate::lib_ctx::LibCtx>,
42 name: &CStr,
43 props: Option<&CStr>,
44 ) -> Result<Self, ErrorStack> {
45 let props_ptr = props.map_or(std::ptr::null(), CStr::as_ptr);
46 let ptr = unsafe { sys::EVP_MAC_fetch(ctx.as_ptr(), name.as_ptr(), props_ptr) };
47 if ptr.is_null() {
48 return Err(ErrorStack::drain());
49 }
50 Ok(MacAlg {
51 ptr,
52 lib_ctx: Some(Arc::clone(ctx)),
53 })
54 }
55
56 #[must_use]
58 pub fn as_ptr(&self) -> *const sys::EVP_MAC {
59 self.ptr
60 }
61
62 #[must_use]
66 pub fn name(&self) -> &CStr {
67 unsafe { CStr::from_ptr(sys::EVP_MAC_get0_name(self.ptr)) }
71 }
72}
73
74impl Clone for MacAlg {
75 fn clone(&self) -> Self {
76 unsafe { sys::EVP_MAC_up_ref(self.ptr) };
77 MacAlg {
78 ptr: self.ptr,
79 lib_ctx: self.lib_ctx.clone(),
80 }
81 }
82}
83
84impl Drop for MacAlg {
85 fn drop(&mut self) {
86 unsafe { sys::EVP_MAC_free(self.ptr) };
87 }
88}
89
90unsafe impl Send for MacAlg {}
92unsafe impl Sync for MacAlg {}
93
94pub struct MacCtx {
101 ptr: *mut sys::EVP_MAC_CTX,
102}
103
104impl MacCtx {
105 pub fn new(alg: &MacAlg) -> Result<Self, ErrorStack> {
111 let ptr = unsafe { sys::EVP_MAC_CTX_new(alg.ptr) };
112 if ptr.is_null() {
113 return Err(ErrorStack::drain());
114 }
115 Ok(MacCtx { ptr })
116 }
117
118 pub fn init(
125 &mut self,
126 key: &[u8],
127 params: Option<&crate::params::Params<'_>>,
128 ) -> Result<(), ErrorStack> {
129 let params_ptr = params.map_or(crate::params::null_params(), crate::params::Params::as_ptr);
130 crate::ossl_call!(sys::EVP_MAC_init(
131 self.ptr,
132 key.as_ptr(),
133 key.len(),
134 params_ptr
135 ))
136 }
137
138 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
142 crate::ossl_call!(sys::EVP_MAC_update(self.ptr, data.as_ptr(), data.len()))
143 }
144
145 pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
152 let mut outl: usize = 0;
153 crate::ossl_call!(sys::EVP_MAC_final(
154 self.ptr,
155 out.as_mut_ptr(),
156 std::ptr::addr_of_mut!(outl),
157 out.len()
158 ))?;
159 Ok(outl)
160 }
161
162 pub fn finish_xof(&mut self, out: &mut [u8]) -> Result<(), ErrorStack> {
166 crate::ossl_call!(sys::EVP_MAC_finalXOF(self.ptr, out.as_mut_ptr(), out.len()))
167 }
168
169 #[must_use]
171 pub fn mac_size(&self) -> usize {
172 unsafe { sys::EVP_MAC_CTX_get_mac_size(self.ptr) }
173 }
174
175 #[must_use]
183 pub fn block_size(&self) -> usize {
184 unsafe { sys::EVP_MAC_CTX_get_block_size(self.ptr) }
189 }
190
191 #[must_use]
199 pub fn alg(&self) -> Option<MacAlg> {
200 let ptr = unsafe { sys::EVP_MAC_CTX_get0_mac(self.ptr) };
207 if ptr.is_null() {
208 return None;
209 }
210 unsafe { sys::EVP_MAC_up_ref(ptr) };
213 Some(MacAlg { ptr, lib_ctx: None })
214 }
215
216 pub fn fork(&self) -> Result<MacCtx, ErrorStack> {
222 let ptr = unsafe { sys::EVP_MAC_CTX_dup(self.ptr) };
223 if ptr.is_null() {
224 return Err(ErrorStack::drain());
225 }
226 Ok(MacCtx { ptr })
227 }
228}
229
230impl Drop for MacCtx {
231 fn drop(&mut self) {
232 unsafe { sys::EVP_MAC_CTX_free(self.ptr) };
233 }
234}
235
236unsafe impl Send for MacCtx {}
237
238pub struct HmacCtx(MacCtx);
242
243impl HmacCtx {
244 pub fn new(digest: &crate::digest::DigestAlg, key: &[u8]) -> Result<Self, ErrorStack> {
250 let alg = MacAlg::fetch(c"HMAC", None)?;
252 let mut ctx = MacCtx::new(&alg)?;
253
254 let nid = digest.nid();
257 let name_cstr: std::ffi::CString = {
258 let name_ptr = unsafe { native_ossl_sys::OBJ_nid2sn(nid) };
259 if name_ptr.is_null() {
260 return Err(crate::error::ErrorStack::drain());
261 }
262 unsafe { std::ffi::CStr::from_ptr(name_ptr) }.to_owned()
263 };
264
265 let params = crate::params::ParamBuilder::new()?
266 .push_utf8_string(c"digest", &name_cstr)?
267 .build()?;
268
269 ctx.init(key, Some(¶ms))?;
270 Ok(HmacCtx(ctx))
271 }
272
273 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
277 self.0.update(data)
278 }
279
280 pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
284 self.0.finish(out)
285 }
286
287 pub fn finish_to_vec(&mut self) -> Result<Vec<u8>, ErrorStack> {
291 let size = self.0.mac_size();
292 let mut out = vec![0u8; size];
293 let n = self.0.finish(&mut out)?;
294 out.truncate(n);
295 Ok(out)
296 }
297
298 #[must_use]
300 pub fn mac_size(&self) -> usize {
301 self.0.mac_size()
302 }
303
304 pub fn oneshot(
308 digest: &crate::digest::DigestAlg,
309 key: &[u8],
310 data: &[u8],
311 out: &mut [u8],
312 ) -> Result<usize, ErrorStack> {
313 let mut ctx = HmacCtx::new(digest, key)?;
314 ctx.update(data)?;
315 ctx.finish(out)
316 }
317}
318
319pub struct CmacCtx(MacCtx);
323
324impl CmacCtx {
325 pub fn new(cipher: &crate::cipher::CipherAlg, key: &[u8]) -> Result<Self, ErrorStack> {
329 let alg = MacAlg::fetch(c"CMAC", None)?;
330 let mut ctx = MacCtx::new(&alg)?;
331
332 let key_len = cipher.key_len();
341 let cipher_name: &std::ffi::CStr = match key_len {
346 16 => c"AES-128-CBC",
347 24 => c"AES-192-CBC",
348 32 => c"AES-256-CBC",
349 _ => return Err(ErrorStack::drain()),
350 };
351
352 let params = crate::params::ParamBuilder::new()?
353 .push_utf8_string(c"cipher", cipher_name)?
354 .build()?;
355
356 ctx.init(key, Some(¶ms))?;
357 Ok(CmacCtx(ctx))
358 }
359
360 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
364 self.0.update(data)
365 }
366
367 pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
371 self.0.finish(out)
372 }
373}
374
375#[cfg(test)]
378mod tests {
379 use super::*;
380 use crate::digest::DigestAlg;
381 use crate::util::hex_encode;
382
383 #[test]
384 fn fetch_hmac_succeeds() {
385 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
386 drop(alg);
387 }
388
389 #[test]
390 fn fetch_nonexistent_fails() {
391 assert!(MacAlg::fetch(c"NONEXISTENT_MAC_XYZ", None).is_err());
392 }
393
394 #[test]
395 fn clone_then_drop_both() {
396 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
397 let alg2 = alg.clone();
398 drop(alg);
399 drop(alg2);
400 }
401
402 #[test]
407 fn hmac_sha256_rfc4231_tv1() {
408 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
409 let key = [0x0b_u8; 20];
410 let data = b"Hi There";
411
412 let mut ctx = HmacCtx::new(&digest, &key).unwrap();
413 ctx.update(data).unwrap();
414 let mac = ctx.finish_to_vec().unwrap();
415
416 assert_eq!(
417 hex_encode(&mac),
418 "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
419 );
420 }
421
422 #[test]
424 fn hmac_sha256_oneshot() {
425 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
426 let key = [0x0b_u8; 20];
427 let mut out = [0u8; 32];
428 let n = HmacCtx::oneshot(&digest, &key, b"Hi There", &mut out).unwrap();
429 assert_eq!(n, 32);
430 assert_eq!(
431 hex_encode(out),
432 "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"
433 );
434 }
435
436 #[test]
437 fn mac_ctx_block_size_before_init_is_zero() {
438 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
439 let ctx = MacCtx::new(&alg).unwrap();
440 assert_eq!(ctx.block_size(), 0);
443 }
444
445 #[test]
446 fn mac_ctx_block_size_hmac_sha256() {
447 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
448 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
449 let mut ctx = MacCtx::new(&alg).unwrap();
450 let nid = digest.nid();
451 let name_ptr = unsafe { native_ossl_sys::OBJ_nid2sn(nid) };
452 let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
453 let params = crate::params::ParamBuilder::new()
454 .unwrap()
455 .push_utf8_string(c"digest", name)
456 .unwrap()
457 .build()
458 .unwrap();
459 ctx.init(&[0u8; 32], Some(¶ms)).unwrap();
460 assert_eq!(ctx.block_size(), 64);
462 }
463
464 #[test]
465 fn mac_ctx_block_size_hmac_sha512() {
466 let digest = DigestAlg::fetch(c"SHA2-512", None).unwrap();
467 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
468 let mut ctx = MacCtx::new(&alg).unwrap();
469 let nid = digest.nid();
470 let name_ptr = unsafe { native_ossl_sys::OBJ_nid2sn(nid) };
471 let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
472 let params = crate::params::ParamBuilder::new()
473 .unwrap()
474 .push_utf8_string(c"digest", name)
475 .unwrap()
476 .build()
477 .unwrap();
478 ctx.init(&[0u8; 64], Some(¶ms)).unwrap();
479 assert_eq!(ctx.block_size(), 128);
481 }
482
483 #[test]
484 fn mac_ctx_alg_returns_hmac_name() {
485 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
486 let ctx = MacCtx::new(&alg).unwrap();
487 let retrieved = ctx.alg().expect("alg() should return Some after new()");
488 assert_eq!(retrieved.name().to_bytes(), b"HMAC");
489 }
490
491 #[test]
492 fn mac_ctx_alg_outlives_context() {
493 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
494 let ctx = MacCtx::new(&alg).unwrap();
495 let retrieved = ctx.alg().unwrap();
496 drop(ctx);
498 assert_eq!(retrieved.name().to_bytes(), b"HMAC");
499 drop(retrieved);
500 }
501
502 #[test]
504 fn mac_ctx_fork_mid_stream() {
505 let alg = MacAlg::fetch(c"HMAC", None).unwrap();
506 let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
507 let nid = digest.nid();
508 let name_ptr = unsafe { native_ossl_sys::OBJ_nid2sn(nid) };
509 let name = unsafe { std::ffi::CStr::from_ptr(name_ptr) };
510 let params = crate::params::ParamBuilder::new()
511 .unwrap()
512 .push_utf8_string(c"digest", name)
513 .unwrap()
514 .build()
515 .unwrap();
516
517 let mut ctx = MacCtx::new(&alg).unwrap();
518 ctx.init(&[0u8; 32], Some(¶ms)).unwrap();
519 ctx.update(b"common").unwrap();
520
521 let mut fork = ctx.fork().unwrap();
522 ctx.update(b" A").unwrap();
523 fork.update(b" B").unwrap();
524
525 let mut out_a = [0u8; 32];
526 let mut out_b = [0u8; 32];
527 ctx.finish(&mut out_a).unwrap();
528 fork.finish(&mut out_b).unwrap();
529
530 assert_ne!(out_a, out_b);
531 }
532}