ring-native-ossl 0.1.10

A ring-compatible API backed by native-ossl (OpenSSL)
Documentation
//! HMAC message authentication, mirroring `ring::hmac`.
//!
//! Provides HMAC-SHA256, HMAC-SHA384, and HMAC-SHA512.  Key material is stored
//! in [`native_ossl::util::SecretBuf`] and is erased on drop.  The
//! [`verify`] function performs a constant-time comparison to prevent
//! timing-based forgery attacks.

use crate::error::Unspecified;
use native_ossl::digest::DigestAlg;
use native_ossl::mac::HmacCtx;
use native_ossl::util::SecretBuf;

/// HMAC algorithm descriptor, mirroring `ring::hmac::Algorithm`.
#[derive(Debug, Clone, Copy)]
pub struct Algorithm(pub(crate) &'static crate::digest::Algorithm);

pub static HMAC_SHA256: Algorithm = Algorithm(&crate::digest::SHA256);
pub static HMAC_SHA384: Algorithm = Algorithm(&crate::digest::SHA384);
pub static HMAC_SHA512: Algorithm = Algorithm(&crate::digest::SHA512);

/// An HMAC signing key, mirroring `ring::hmac::Key`.
pub struct Key {
    // Debug implemented manually below to redact key material
    alg: Algorithm,
    key_bytes: SecretBuf,
}

impl std::fmt::Debug for Key {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Key")
            .field("alg", &self.alg)
            .finish_non_exhaustive()
    }
}

impl Key {
    #[must_use]
    pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Self {
        Self {
            alg: algorithm,
            key_bytes: SecretBuf::from_slice(key_value),
        }
    }

    #[must_use]
    pub fn algorithm(&self) -> Algorithm {
        self.alg
    }
}

/// An HMAC tag (output), mirroring `ring::hmac::Tag`.
#[derive(Debug)]
pub struct Tag {
    bytes: [u8; 64],
    len: usize,
}

impl AsRef<[u8]> for Tag {
    fn as_ref(&self) -> &[u8] {
        &self.bytes[..self.len]
    }
}

/// Compute HMAC in one shot, mirroring `ring::hmac::sign`.
///
/// # Panics
///
/// Panics if OpenSSL cannot load the digest algorithm or the HMAC operation fails.
#[must_use]
pub fn sign(key: &Key, data: &[u8]) -> Tag {
    let digest_alg = DigestAlg::fetch(key.alg.0.name, None)
        .unwrap_or_else(|e| panic!("OpenSSL digest unavailable: {e}"));
    let mut bytes = [0u8; 64];
    let len = HmacCtx::oneshot(&digest_alg, key.key_bytes.as_ref(), data, &mut bytes)
        .unwrap_or_else(|e| panic!("HmacCtx::oneshot failed: {e}"));
    Tag { bytes, len }
}

/// Verify HMAC in one shot (constant-time), mirroring `ring::hmac::verify`.
///
/// # Errors
///
/// Returns `Unspecified` if the computed HMAC does not match `tag`.
pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), Unspecified> {
    let computed = sign(key, data);
    if native_ossl::util::ct_eq(computed.as_ref(), tag) {
        Ok(())
    } else {
        Err(Unspecified)
    }
}