Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 typeOpenSSL nameSub-algorithm parameter
HMACc"HMAC"digest = c"SHA2-256" (set via HmacCtx)
CMACc"CMAC"cipher = c"AES-256-CBC" (set via CmacCtx)
GMACc"GMAC"cipher = c"AES-256-GCM"
KMAC-128c"KMAC128"none
KMAC-256c"KMAC256"none
Poly1305c"POLY1305"none
SipHashc"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(&params))?;
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::new is 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 to EVP_MAC_init. The key is always copied into the MAC context for correct zeroization on drop.
  • mac_size after init — for some algorithms, EVP_MAC_CTX_get_mac_size only returns a meaningful value after EVP_MAC_init. Always call init first.
  • fork vs cloneEVP_MAC_CTX_dup performs a deep copy of the MAC state. Naming it fork rather than clone signals that the operation is explicit and potentially expensive.
  • KMAC customisation string — pass it through Params at init time using push_utf8_string(c"custom", c"my_context").
  • block_size() before initEVP_MAC_CTX_get_block_size returns 0 when the sub-algorithm (e.g. the digest for HMAC) has not yet been set via init. Always call init before reading block_size() for HMAC/CMAC contexts.
  • alg() ownershipEVP_MAC_CTX_get0_mac returns a borrowed pointer; alg() immediately calls EVP_MAC_up_ref so the returned MacAlg owns its own reference and may outlive the MacCtx. This is the same get0 + up_ref pattern used by DigestCtx::alg().