1//! Message digest algorithms.
2
3#[cfg(ossl300)]
4use crate::cvt_p;
5#[cfg(ossl300)]
6use crate::error::ErrorStack;
7#[cfg(ossl300)]
8use crate::lib_ctx::LibCtxRef;
9use crate::nid::Nid;
10use cfg_if::cfg_if;
11use foreign_types::{ForeignTypeRef, Opaque};
12use openssl_macros::corresponds;
13#[cfg(ossl300)]
14use std::ffi::CString;
15#[cfg(ossl300)]
16use std::ptr;
17
18cfg_if! {
19    if #[cfg(ossl300)] {
20        use foreign_types::ForeignType;
21        use std::ops::{Deref, DerefMut};
22
23        type Inner = *mut ffi::EVP_MD;
24
25        impl Drop for Md {
26            #[inline]
27            fn drop(&mut self) {
28                unsafe {
29                    ffi::EVP_MD_free(self.as_ptr());
30                }
31            }
32        }
33
34        impl ForeignType for Md {
35            type CType = ffi::EVP_MD;
36            type Ref = MdRef;
37
38            #[inline]
39            unsafe fn from_ptr(ptr: *mut Self::CType) -> Self {
40                Md(ptr)
41            }
42
43            #[inline]
44            fn as_ptr(&self) -> *mut Self::CType {
45                self.0
46            }
47        }
48
49        impl Deref for Md {
50            type Target = MdRef;
51
52            #[inline]
53            fn deref(&self) -> &Self::Target {
54                unsafe {
55                    MdRef::from_ptr(self.as_ptr())
56                }
57            }
58        }
59
60        impl DerefMut for Md {
61            #[inline]
62            fn deref_mut(&mut self) -> &mut Self::Target {
63                unsafe {
64                    MdRef::from_ptr_mut(self.as_ptr())
65                }
66            }
67        }
68    } else {
69        enum Inner {}
70    }
71}
72
73/// A message digest algorithm.
74pub struct Md(Inner);
75
76unsafe impl Sync for Md {}
77unsafe impl Send for Md {}
78
79impl Md {
80    /// Returns the `Md` corresponding to an [`Nid`].
81    #[corresponds(EVP_get_digestbynid)]
82    pub fn from_nid(type_: Nid) -> Option<&'static MdRef> {
83        unsafe {
84            let ptr = ffi::EVP_get_digestbynid(type_.as_raw());
85            if ptr.is_null() {
86                None
87            } else {
88                Some(MdRef::from_ptr(ptr as *mut _))
89            }
90        }
91    }
92
93    /// Fetches an `Md` object corresponding to the specified algorithm name and properties.
94    ///
95    /// Requires OpenSSL 3.0.0 or newer.
96    #[corresponds(EVP_MD_fetch)]
97    #[cfg(ossl300)]
98    pub fn fetch(
99        ctx: Option<&LibCtxRef>,
100        algorithm: &str,
101        properties: Option<&str>,
102    ) -> Result<Self, ErrorStack> {
103        let algorithm = CString::new(algorithm).unwrap();
104        let properties = properties.map(|s| CString::new(s).unwrap());
105
106        unsafe {
107            let ptr = cvt_p(ffi::EVP_MD_fetch(
108                ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr),
109                algorithm.as_ptr(),
110                properties.map_or(ptr::null_mut(), |s| s.as_ptr()),
111            ))?;
112
113            Ok(Md::from_ptr(ptr))
114        }
115    }
116
117    #[inline]
118    #[cfg(not(boringssl))]
119    pub fn null() -> &'static MdRef {
120        unsafe { MdRef::from_ptr(ffi::EVP_md_null() as *mut _) }
121    }
122
123    #[inline]
124    pub fn md5() -> &'static MdRef {
125        unsafe { MdRef::from_ptr(ffi::EVP_md5() as *mut _) }
126    }
127
128    #[inline]
129    pub fn sha1() -> &'static MdRef {
130        unsafe { MdRef::from_ptr(ffi::EVP_sha1() as *mut _) }
131    }
132
133    #[inline]
134    pub fn sha224() -> &'static MdRef {
135        unsafe { MdRef::from_ptr(ffi::EVP_sha224() as *mut _) }
136    }
137
138    #[inline]
139    pub fn sha256() -> &'static MdRef {
140        unsafe { MdRef::from_ptr(ffi::EVP_sha256() as *mut _) }
141    }
142
143    #[inline]
144    pub fn sha384() -> &'static MdRef {
145        unsafe { MdRef::from_ptr(ffi::EVP_sha384() as *mut _) }
146    }
147
148    #[inline]
149    pub fn sha512() -> &'static MdRef {
150        unsafe { MdRef::from_ptr(ffi::EVP_sha512() as *mut _) }
151    }
152
153    #[cfg(ossl111)]
154    #[inline]
155    pub fn sha3_224() -> &'static MdRef {
156        unsafe { MdRef::from_ptr(ffi::EVP_sha3_224() as *mut _) }
157    }
158
159    #[cfg(ossl111)]
160    #[inline]
161    pub fn sha3_256() -> &'static MdRef {
162        unsafe { MdRef::from_ptr(ffi::EVP_sha3_256() as *mut _) }
163    }
164
165    #[cfg(ossl111)]
166    #[inline]
167    pub fn sha3_384() -> &'static MdRef {
168        unsafe { MdRef::from_ptr(ffi::EVP_sha3_384() as *mut _) }
169    }
170
171    #[cfg(ossl111)]
172    #[inline]
173    pub fn sha3_512() -> &'static MdRef {
174        unsafe { MdRef::from_ptr(ffi::EVP_sha3_512() as *mut _) }
175    }
176
177    #[cfg(ossl111)]
178    #[inline]
179    pub fn shake128() -> &'static MdRef {
180        unsafe { MdRef::from_ptr(ffi::EVP_shake128() as *mut _) }
181    }
182
183    #[cfg(ossl111)]
184    #[inline]
185    pub fn shake256() -> &'static MdRef {
186        unsafe { MdRef::from_ptr(ffi::EVP_shake256() as *mut _) }
187    }
188
189    #[cfg(not(osslconf = "OPENSSL_NO_RMD160"))]
190    #[inline]
191    #[cfg(not(boringssl))]
192    pub fn ripemd160() -> &'static MdRef {
193        unsafe { MdRef::from_ptr(ffi::EVP_ripemd160() as *mut _) }
194    }
195
196    #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM3")))]
197    #[inline]
198    #[cfg(not(boringssl))]
199    pub fn sm3() -> &'static MdRef {
200        unsafe { MdRef::from_ptr(ffi::EVP_sm3() as *mut _) }
201    }
202}
203
204/// A reference to an [`Md`].
205pub struct MdRef(Opaque);
206
207impl ForeignTypeRef for MdRef {
208    type CType = ffi::EVP_MD;
209}
210
211unsafe impl Sync for MdRef {}
212unsafe impl Send for MdRef {}
213
214impl MdRef {
215    /// Returns the block size of the digest in bytes.
216    #[corresponds(EVP_MD_block_size)]
217    #[inline]
218    pub fn block_size(&self) -> usize {
219        unsafe { ffi::EVP_MD_block_size(self.as_ptr()) as usize }
220    }
221
222    /// Returns the size of the digest in bytes.
223    #[corresponds(EVP_MD_size)]
224    #[inline]
225    pub fn size(&self) -> usize {
226        unsafe { ffi::EVP_MD_size(self.as_ptr()) as usize }
227    }
228
229    /// Returns the [`Nid`] of the digest.
230    #[corresponds(EVP_MD_type)]
231    #[inline]
232    pub fn type_(&self) -> Nid {
233        unsafe { Nid::from_raw(ffi::EVP_MD_type(self.as_ptr())) }
234    }
235}
236