Skip to main content

ring_native_ossl/
digest.rs

1//! Cryptographic hash functions, mirroring `ring::digest`.
2//!
3//! Provides SHA-256, SHA-384, SHA-512, and SHA-1 (the last only for legacy
4//! compatibility).  Both one-shot ([`digest`]) and incremental ([`Context`])
5//! interfaces are available.  Panics on internal OpenSSL errors because those
6//! indicate a non-functional OpenSSL installation rather than bad caller input.
7
8use native_ossl::digest::{DigestAlg, DigestCtx};
9use std::ffi::CStr;
10
11/// A digest algorithm descriptor, mirroring `ring::digest::Algorithm`.
12#[derive(Debug)]
13pub struct Algorithm {
14    pub(crate) name: &'static CStr,
15    pub output_len: usize,
16    pub block_len: usize,
17}
18
19pub static SHA256: Algorithm = Algorithm {
20    name: c"SHA2-256",
21    output_len: 32,
22    block_len: 64,
23};
24pub static SHA384: Algorithm = Algorithm {
25    name: c"SHA2-384",
26    output_len: 48,
27    block_len: 128,
28};
29pub static SHA512: Algorithm = Algorithm {
30    name: c"SHA2-512",
31    output_len: 64,
32    block_len: 128,
33};
34pub static SHA1_FOR_LEGACY_USE_ONLY: Algorithm = Algorithm {
35    name: c"SHA1",
36    output_len: 20,
37    block_len: 64,
38};
39
40/// Digest output bytes.
41pub struct Digest {
42    value: [u8; 64],
43    len: usize,
44    alg: &'static Algorithm,
45}
46
47impl Digest {
48    #[must_use]
49    pub fn algorithm(&self) -> &'static Algorithm {
50        self.alg
51    }
52}
53
54impl AsRef<[u8]> for Digest {
55    fn as_ref(&self) -> &[u8] {
56        &self.value[..self.len]
57    }
58}
59
60/// Incremental digest context, mirroring `ring::digest::Context`.
61pub struct Context {
62    alg: &'static Algorithm,
63    ctx: DigestCtx,
64}
65
66impl Context {
67    /// # Panics
68    ///
69    /// Panics if OpenSSL cannot load the digest algorithm or create a context,
70    /// which indicates a non-functional OpenSSL installation.
71    #[must_use]
72    pub fn new(algorithm: &'static Algorithm) -> Self {
73        let alg_obj = DigestAlg::fetch(algorithm.name, None)
74            .unwrap_or_else(|e| panic!("OpenSSL digest algorithm unavailable: {e}"));
75        let ctx = alg_obj
76            .new_context()
77            .unwrap_or_else(|e| panic!("EVP_MD_CTX_new failed: {e}"));
78        Self {
79            alg: algorithm,
80            ctx,
81        }
82    }
83
84    /// # Panics
85    ///
86    /// Panics if OpenSSL's `EVP_DigestUpdate` fails.
87    pub fn update(&mut self, data: &[u8]) {
88        self.ctx
89            .update(data)
90            .unwrap_or_else(|e| panic!("EVP_DigestUpdate failed: {e}"));
91    }
92
93    /// # Panics
94    ///
95    /// Panics if OpenSSL's `EVP_MD_CTX_copy_ex` fails.
96    #[must_use]
97    pub fn clone_state(&self) -> Self {
98        Self {
99            alg: self.alg,
100            ctx: self
101                .ctx
102                .fork()
103                .unwrap_or_else(|e| panic!("EVP_MD_CTX_copy_ex failed: {e}")),
104        }
105    }
106
107    /// # Panics
108    ///
109    /// Panics if OpenSSL's `EVP_DigestFinal_ex` fails.
110    #[must_use]
111    pub fn finish(mut self) -> Digest {
112        let mut out = [0u8; 64];
113        let n = self
114            .ctx
115            .finish(&mut out)
116            .unwrap_or_else(|e| panic!("EVP_DigestFinal_ex failed: {e}"));
117        Digest {
118            value: out,
119            len: n,
120            alg: self.alg,
121        }
122    }
123
124    #[must_use]
125    pub fn algorithm(&self) -> &'static Algorithm {
126        self.alg
127    }
128}
129
130impl Clone for Context {
131    fn clone(&self) -> Self {
132        self.clone_state()
133    }
134}
135
136/// One-shot digest computation, mirroring `ring::digest::digest`.
137#[must_use]
138pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
139    let mut ctx = Context::new(algorithm);
140    ctx.update(data);
141    ctx.finish()
142}