Message Authentication
Overview
MAC operations use the same fetch-then-use pattern as digests:
MacAlg— algorithm descriptor. Fetch once, reuse.Send + Sync.MacCtx— stateful context. Exclusive ownership; supports mid-stream forking.HmacCtx/CmacCtx— higher-level wrappers that handle sub-algorithm parameters for HMAC and CMAC respectively.
MacAlg — Algorithm Descriptor
#![allow(unused)]
fn main() {
pub struct MacAlg { /* EVP_MAC* */ }
impl MacAlg {
/// Fetch from the global default library context.
pub fn fetch(name: &CStr, props: Option<&CStr>) -> Result<Self, ErrorStack>;
/// Fetch from an explicit library context (for FIPS isolation).
pub fn fetch_in(ctx: &Arc<LibCtx>, name: &CStr, props: Option<&CStr>)
-> Result<Self, ErrorStack>;
}
}
MacCtx — Raw Stateful Context
MacCtx::new is public. Use it when you need a raw context for algorithms that
HmacCtx and CmacCtx do not cover (KMAC, GMAC, Poly1305, SipHash), or when
you want to pass parameters manually.
#![allow(unused)]
fn main() {
pub struct MacCtx { /* EVP_MAC_CTX* */ }
impl MacCtx {
/// Create a new (uninitialised) MAC context from an algorithm descriptor.
///
/// Call `init` with a key before feeding data.
pub fn new(alg: &MacAlg) -> Result<Self, ErrorStack>;
/// Initialise (or re-initialise) with a key and optional parameters.
///
/// The key is always copied into OpenSSL's MAC context state.
/// For HMAC, pass the digest name via params. For CMAC, pass the cipher name.
pub fn init(&mut self, key: &[u8], params: Option<&Params<'_>>) -> Result<(), ErrorStack>;
/// Feed data into the MAC computation. Zero-copy from caller's slice.
pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack>;
/// Finalise the MAC. `out` must be at least `self.mac_size()` bytes.
pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack>;
/// Finalise with variable output length (for XOF MACs: KMAC-128, KMAC-256).
pub fn finish_xof(&mut self, out: &mut [u8]) -> Result<(), ErrorStack>;
/// Expected MAC output length. Available after `init`.
pub fn mac_size(&self) -> usize;
/// Block size of the underlying MAC algorithm in bytes. Returns `0` before `init`.
pub fn block_size(&self) -> usize;
/// Return the algorithm descriptor for this context (independent ownership).
pub fn alg(&self) -> Option<MacAlg>;
/// Fork the current mid-stream state into a new independent context.
pub fn fork(&self) -> Result<MacCtx, ErrorStack>;
}
}
HmacCtx — HMAC Helper
HMAC always requires a digest algorithm. HmacCtx bundles this requirement:
#![allow(unused)]
fn main() {
pub struct HmacCtx(MacCtx);
impl HmacCtx {
/// Create an HMAC context using the given digest algorithm and key.
pub fn new(digest: &DigestAlg, key: &[u8]) -> Result<Self, ErrorStack>;
pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack>;
pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack>;
pub fn finish_to_vec(&mut self) -> Result<Vec<u8>, ErrorStack>;
pub fn mac_size(&self) -> usize;
/// One-shot HMAC over a single input.
pub fn oneshot(digest: &DigestAlg, key: &[u8], data: &[u8], out: &mut [u8])
-> Result<usize, ErrorStack>;
}
}
CmacCtx — CMAC Helper
CMAC requires a block cipher (typically AES):
#![allow(unused)]
fn main() {
pub struct CmacCtx(MacCtx);
impl CmacCtx {
/// Create a CMAC context using the given cipher algorithm and key.
pub fn new(cipher: &CipherAlg, key: &[u8]) -> Result<Self, ErrorStack>;
pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack>;
pub fn finish(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack>;
}
}
Algorithm Names
| MAC type | OpenSSL name | Sub-algorithm parameter |
|---|---|---|
| HMAC | c"HMAC" | digest = c"SHA2-256" (set via HmacCtx) |
| CMAC | c"CMAC" | cipher = c"AES-256-CBC" (set via CmacCtx) |
| GMAC | c"GMAC" | cipher = c"AES-256-GCM" |
| KMAC-128 | c"KMAC128" | none |
| KMAC-256 | c"KMAC256" | none |
| Poly1305 | c"POLY1305" | none |
| SipHash | c"SIPHASH" | none |
Examples
HMAC-SHA-256
#![allow(unused)]
fn main() {
use native_ossl::mac::HmacCtx;
use native_ossl::digest::DigestAlg;
let sha256 = DigestAlg::fetch(c"SHA2-256", None)?;
let key = b"secret key";
let data = b"message";
// Streaming
let mut hmac = HmacCtx::new(&sha256, key)?;
hmac.update(data)?;
let mac = hmac.finish_to_vec()?;
// One-shot
let mut out = [0u8; 32];
HmacCtx::oneshot(&sha256, key, data, &mut out)?;
}
KMAC-256 (XOF, Variable Output)
#![allow(unused)]
fn main() {
use native_ossl::mac::{MacAlg, MacCtx};
let alg = MacAlg::fetch(c"KMAC256", None)?;
let mut ctx = MacCtx::new(&alg)?;
ctx.init(b"my key", None)?;
ctx.update(b"input data")?;
// Caller chooses the output length.
let mut out = vec![0u8; 64];
ctx.finish_xof(&mut out)?;
}
Raw MacCtx for Poly1305
MacCtx::new is the entry point for algorithms not covered by the typed helpers.
Poly1305 takes no sub-algorithm parameter — pass None to init:
use native_ossl::mac::{MacAlg, MacCtx};
let alg = MacAlg::fetch(c"POLY1305", None)?;
let mut ctx = MacCtx::new(&alg)?;
// Poly1305 key is always 32 bytes.
let key = [0u8; 32];
ctx.init(&key, None)?;
ctx.update(b"authenticated message")?;
let mut tag = vec![0u8; ctx.mac_size()];
ctx.finish(&mut tag)?;
Fork Mid-Stream
#![allow(unused)]
fn main() {
let sha256 = DigestAlg::fetch(c"SHA2-256", None)?;
let mut ctx = HmacCtx::new(&sha256, b"key")?;
// ... (note: raw MacCtx supports fork; HmacCtx wraps MacCtx internally)
let alg = MacAlg::fetch(c"HMAC", None)?;
let mut mac_ctx = MacCtx::new(&alg)?;
// Build params manually for raw MacCtx
use native_ossl::params::ParamBuilder;
let params = ParamBuilder::new()?
.push_utf8_string(c"digest", c"SHA2-256")?
.build()?;
mac_ctx.init(b"key", Some(¶ms))?;
mac_ctx.update(b"common prefix")?;
// Fork at the current state.
let mut fork = mac_ctx.fork()?;
mac_ctx.update(b" path A")?;
fork.update(b" path B")?;
let mut mac_a = [0u8; 32];
let mut mac_b = [0u8; 32];
mac_ctx.finish(&mut mac_a)?;
fork.finish(&mut mac_b)?;
}
Design Notes
MacCtx::newis public — previously crate-internal, it is now part of the public API. Use it directly when you need a raw context for algorithms that the typed helpers (HmacCtx,CmacCtx) do not cover.- Key at
init, not construction — OpenSSL passes the key toEVP_MAC_init. The key is always copied into the MAC context for correct zeroization on drop. mac_sizeafterinit— for some algorithms,EVP_MAC_CTX_get_mac_sizeonly returns a meaningful value afterEVP_MAC_init. Always callinitfirst.forkvsclone—EVP_MAC_CTX_dupperforms a deep copy of the MAC state. Naming itforkrather thanclonesignals that the operation is explicit and potentially expensive.- KMAC customisation string — pass it through
Paramsatinittime usingpush_utf8_string(c"custom", c"my_context"). block_size()beforeinit—EVP_MAC_CTX_get_block_sizereturns0when the sub-algorithm (e.g. the digest for HMAC) has not yet been set viainit. Always callinitbefore readingblock_size()for HMAC/CMAC contexts.alg()ownership —EVP_MAC_CTX_get0_macreturns a borrowed pointer;alg()immediately callsEVP_MAC_up_refso the returnedMacAlgowns its own reference and may outlive theMacCtx. This is the sameget0+up_refpattern used byDigestCtx::alg().