ring-native-ossl 0.1.8

A ring-compatible API backed by native-ossl (OpenSSL)
Documentation
//! Cryptographic hash functions, mirroring `ring::digest`.
//!
//! Provides SHA-256, SHA-384, SHA-512, and SHA-1 (the last only for legacy
//! compatibility).  Both one-shot ([`digest`]) and incremental ([`Context`])
//! interfaces are available.  Panics on internal OpenSSL errors because those
//! indicate a non-functional OpenSSL installation rather than bad caller input.

use native_ossl::digest::{DigestAlg, DigestCtx};
use std::ffi::CStr;

/// A digest algorithm descriptor, mirroring `ring::digest::Algorithm`.
#[derive(Debug)]
pub struct Algorithm {
    pub(crate) name: &'static CStr,
    pub output_len: usize,
    pub block_len: usize,
}

pub static SHA256: Algorithm = Algorithm {
    name: c"SHA2-256",
    output_len: 32,
    block_len: 64,
};
pub static SHA384: Algorithm = Algorithm {
    name: c"SHA2-384",
    output_len: 48,
    block_len: 128,
};
pub static SHA512: Algorithm = Algorithm {
    name: c"SHA2-512",
    output_len: 64,
    block_len: 128,
};
pub static SHA1_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
    name: c"SHA1",
    output_len: 20,
    block_len: 64,
};

/// Digest output bytes.
pub struct Digest {
    value: [u8; 64],
    len: usize,
    alg: &'static Algorithm,
}

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

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

/// Incremental digest context, mirroring `ring::digest::Context`.
pub struct Context {
    alg: &'static Algorithm,
    ctx: DigestCtx,
}

impl Context {
    /// # Panics
    ///
    /// Panics if OpenSSL cannot load the digest algorithm or create a context,
    /// which indicates a non-functional OpenSSL installation.
    #[must_use]
    pub fn new(algorithm: &'static Algorithm) -> Self {
        let alg_obj = DigestAlg::fetch(algorithm.name, None)
            .unwrap_or_else(|e| panic!("OpenSSL digest algorithm unavailable: {e}"));
        let ctx = alg_obj
            .new_context()
            .unwrap_or_else(|e| panic!("EVP_MD_CTX_new failed: {e}"));
        Self {
            alg: algorithm,
            ctx,
        }
    }

    /// # Panics
    ///
    /// Panics if OpenSSL's `EVP_DigestUpdate` fails.
    pub fn update(&mut self, data: &[u8]) {
        self.ctx
            .update(data)
            .unwrap_or_else(|e| panic!("EVP_DigestUpdate failed: {e}"));
    }

    /// # Panics
    ///
    /// Panics if OpenSSL's `EVP_MD_CTX_copy_ex` fails.
    #[must_use]
    pub fn clone_state(&self) -> Self {
        Self {
            alg: self.alg,
            ctx: self
                .ctx
                .fork()
                .unwrap_or_else(|e| panic!("EVP_MD_CTX_copy_ex failed: {e}")),
        }
    }

    /// # Panics
    ///
    /// Panics if OpenSSL's `EVP_DigestFinal_ex` fails.
    #[must_use]
    pub fn finish(mut self) -> Digest {
        let mut out = [0u8; 64];
        let n = self
            .ctx
            .finish(&mut out)
            .unwrap_or_else(|e| panic!("EVP_DigestFinal_ex failed: {e}"));
        Digest {
            value: out,
            len: n,
            alg: self.alg,
        }
    }

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

impl Clone for Context {
    fn clone(&self) -> Self {
        self.clone_state()
    }
}

/// One-shot digest computation, mirroring `ring::digest::digest`.
#[must_use]
pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
    let mut ctx = Context::new(algorithm);
    ctx.update(data);
    ctx.finish()
}