Skip to main content

openssl/
md_ctx.rs

1//! The message digest context.
2//!
3//! # Examples
4//!
5//! Compute the SHA256 checksum of data
6//!
7//! ```
8//! use openssl::md::Md;
9//! use openssl::md_ctx::MdCtx;
10//!
11//! let mut ctx = MdCtx::new().unwrap();
12//! ctx.digest_init(Md::sha256()).unwrap();
13//! ctx.digest_update(b"Some Crypto Text").unwrap();
14//! let mut digest = [0; 32];
15//! ctx.digest_final(&mut digest).unwrap();
16//!
17//! assert_eq!(
18//!     digest,
19//!     *b"\x60\x78\x56\x38\x8a\xca\x5c\x51\x83\xc4\xd1\x4d\xc8\xf9\xcc\xf2\
20//!        \xa5\x21\xb3\x10\x93\x72\xfa\xd6\x7c\x55\xf5\xc9\xe3\xd1\x83\x19",
21//! );
22//! ```
23//!
24//! Sign and verify data with RSA and SHA256
25//!
26//! ```
27//! use openssl::md::Md;
28//! use openssl::md_ctx::MdCtx;
29//! use openssl::pkey::PKey;
30//! use openssl::rsa::Rsa;
31//!
32//! // Generate a random RSA key.
33//! let key = Rsa::generate(4096).unwrap();
34//! let key = PKey::from_rsa(key).unwrap();
35//!
36//! let text = b"Some Crypto Text";
37//!
38//! // Create the signature.
39//! let mut ctx = MdCtx::new().unwrap();
40//! ctx.digest_sign_init(Some(Md::sha256()), &key).unwrap();
41//! ctx.digest_sign_update(text).unwrap();
42//! let mut signature = vec![];
43//! ctx.digest_sign_final_to_vec(&mut signature).unwrap();
44//!
45//! // Verify the signature.
46//! let mut ctx = MdCtx::new().unwrap();
47//! ctx.digest_verify_init(Some(Md::sha256()), &key).unwrap();
48//! ctx.digest_verify_update(text).unwrap();
49//! let valid = ctx.digest_verify_final(&signature).unwrap();
50//! assert!(valid);
51//! ```
52//!
53
54#![cfg_attr(
55    not(any(boringssl, awslc)),
56    doc = r#"\
57Compute and verify an HMAC-SHA256
58
59```
60use openssl::md::Md;
61use openssl::md_ctx::MdCtx;
62use openssl::memcmp;
63use openssl::pkey::PKey;
64
65// Create a key with the HMAC secret.
66let key = PKey::hmac(b"my secret").unwrap();
67
68let text = b"Some Crypto Text";
69
70// Compute the HMAC.
71let mut ctx = MdCtx::new().unwrap();
72ctx.digest_sign_init(Some(Md::sha256()), &key).unwrap();
73ctx.digest_sign_update(text).unwrap();
74let mut hmac = vec![];
75ctx.digest_sign_final_to_vec(&mut hmac).unwrap();
76
77// Verify the HMAC. You can't use MdCtx to do this; instead use a constant time equality check.
78# let target = hmac.clone();
79let valid = memcmp::eq(&hmac, &target);
80assert!(valid);
81```"#
82)]
83
84use crate::error::ErrorStack;
85use crate::md::MdRef;
86use crate::pkey::{HasPrivate, HasPublic, PKeyRef};
87use crate::pkey_ctx::PkeyCtxRef;
88use crate::{cvt, cvt_p};
89use cfg_if::cfg_if;
90use foreign_types::{ForeignType, ForeignTypeRef};
91use openssl_macros::corresponds;
92use std::convert::TryFrom;
93use std::ptr;
94
95cfg_if! {
96    if #[cfg(any(ossl110, boringssl, libressl382, awslc))] {
97        use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new};
98    } else {
99        use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free};
100    }
101}
102
103foreign_type_and_impl_send_sync! {
104    type CType = ffi::EVP_MD_CTX;
105    fn drop = EVP_MD_CTX_free;
106
107    pub struct MdCtx;
108    /// A reference to an [`MdCtx`].
109    pub struct MdCtxRef;
110}
111
112impl MdCtx {
113    /// Creates a new context.
114    #[corresponds(EVP_MD_CTX_new)]
115    #[inline]
116    pub fn new() -> Result<Self, ErrorStack> {
117        ffi::init();
118
119        unsafe {
120            let ptr = cvt_p(EVP_MD_CTX_new())?;
121            Ok(MdCtx::from_ptr(ptr))
122        }
123    }
124}
125
126impl MdCtxRef {
127    /// Initializes the context to compute the digest of data.
128    #[corresponds(EVP_DigestInit_ex)]
129    #[inline]
130    pub fn digest_init(&mut self, digest: &MdRef) -> Result<(), ErrorStack> {
131        unsafe {
132            cvt(ffi::EVP_DigestInit_ex(
133                self.as_ptr(),
134                digest.as_ptr(),
135                ptr::null_mut(),
136            ))?;
137        }
138
139        Ok(())
140    }
141
142    /// Initializes the context to compute the signature of data.
143    ///
144    /// A reference to the context's inner `PkeyCtx` is returned, allowing signature settings to be configured.
145    #[corresponds(EVP_DigestSignInit)]
146    #[inline]
147    pub fn digest_sign_init<'a, T>(
148        &'a mut self,
149        digest: Option<&MdRef>,
150        pkey: &PKeyRef<T>,
151    ) -> Result<&'a mut PkeyCtxRef<T>, ErrorStack>
152    where
153        T: HasPrivate,
154    {
155        unsafe {
156            let mut p = ptr::null_mut();
157            cvt(ffi::EVP_DigestSignInit(
158                self.as_ptr(),
159                &mut p,
160                digest.map_or(ptr::null(), |p| p.as_ptr()),
161                ptr::null_mut(),
162                pkey.as_ptr(),
163            ))?;
164            Ok(PkeyCtxRef::from_ptr_mut(p))
165        }
166    }
167
168    /// Initializes the context to verify the signature of data.
169    ///
170    /// A reference to the context's inner `PkeyCtx` is returned, allowing signature settings to be configured.
171    #[corresponds(EVP_DigestVerifyInit)]
172    #[inline]
173    pub fn digest_verify_init<'a, T>(
174        &'a mut self,
175        digest: Option<&MdRef>,
176        pkey: &PKeyRef<T>,
177    ) -> Result<&'a mut PkeyCtxRef<T>, ErrorStack>
178    where
179        T: HasPublic,
180    {
181        unsafe {
182            let mut p = ptr::null_mut();
183            cvt(ffi::EVP_DigestVerifyInit(
184                self.as_ptr(),
185                &mut p,
186                digest.map_or(ptr::null(), |p| p.as_ptr()),
187                ptr::null_mut(),
188                pkey.as_ptr(),
189            ))?;
190            Ok(PkeyCtxRef::from_ptr_mut(p))
191        }
192    }
193
194    /// Updates the context with more data.
195    #[corresponds(EVP_DigestUpdate)]
196    #[inline]
197    pub fn digest_update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
198        unsafe {
199            cvt(ffi::EVP_DigestUpdate(
200                self.as_ptr(),
201                data.as_ptr() as *const _,
202                data.len(),
203            ))?;
204        }
205
206        Ok(())
207    }
208
209    /// Updates the context with more data.
210    #[corresponds(EVP_DigestSignUpdate)]
211    #[inline]
212    pub fn digest_sign_update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
213        unsafe {
214            cvt(ffi::EVP_DigestSignUpdate(
215                self.as_ptr(),
216                data.as_ptr() as *const _,
217                data.len(),
218            ))?;
219        }
220
221        Ok(())
222    }
223
224    /// Updates the context with more data.
225    #[corresponds(EVP_DigestVerifyUpdate)]
226    #[inline]
227    pub fn digest_verify_update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
228        unsafe {
229            cvt(ffi::EVP_DigestVerifyUpdate(
230                self.as_ptr(),
231                data.as_ptr() as *const _,
232                data.len(),
233            ))?;
234        }
235
236        Ok(())
237    }
238
239    /// Copies the computed digest into the buffer, returning the number of bytes written.
240    #[corresponds(EVP_DigestFinal)]
241    #[inline]
242    pub fn digest_final(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> {
243        let mut len = u32::try_from(out.len()).unwrap_or(u32::MAX);
244
245        if self.size() > len as usize {
246            return Err(ErrorStack::get());
247        }
248
249        unsafe {
250            cvt(ffi::EVP_DigestFinal(
251                self.as_ptr(),
252                out.as_mut_ptr(),
253                &mut len,
254            ))?;
255        }
256
257        Ok(len as usize)
258    }
259
260    /// Copies the computed digest into the buffer.
261    ///
262    /// Requires OpenSSL 1.1.1 or newer.
263    #[corresponds(EVP_DigestFinalXOF)]
264    #[inline]
265    #[cfg(any(ossl111, awslc))]
266    pub fn digest_final_xof(&mut self, out: &mut [u8]) -> Result<(), ErrorStack> {
267        unsafe {
268            cvt(ffi::EVP_DigestFinalXOF(
269                self.as_ptr(),
270                out.as_mut_ptr(),
271                out.len(),
272            ))?;
273        }
274
275        Ok(())
276    }
277
278    /// Signs the computed digest.
279    ///
280    /// If `out` is set to `None`, an upper bound on the number of bytes required for the output buffer will be
281    /// returned.
282    #[corresponds(EVP_DigestSignFinal)]
283    #[inline]
284    pub fn digest_sign_final(&mut self, out: Option<&mut [u8]>) -> Result<usize, ErrorStack> {
285        let mut len = out.as_ref().map_or(0, |b| b.len());
286
287        unsafe {
288            cvt(ffi::EVP_DigestSignFinal(
289                self.as_ptr(),
290                out.map_or(ptr::null_mut(), |b| b.as_mut_ptr()),
291                &mut len,
292            ))?;
293        }
294
295        Ok(len)
296    }
297
298    /// Like [`Self::digest_sign_final`] but appends the signature to a [`Vec`].
299    pub fn digest_sign_final_to_vec(&mut self, out: &mut Vec<u8>) -> Result<usize, ErrorStack> {
300        let base = out.len();
301        let len = self.digest_sign_final(None)?;
302        out.resize(base + len, 0);
303        let len = self.digest_sign_final(Some(&mut out[base..]))?;
304        out.truncate(base + len);
305        Ok(len)
306    }
307
308    /// Verifies the provided signature.
309    ///
310    /// Returns `Ok(true)` if the signature is valid, `Ok(false)` if the signature is invalid, and `Err` if an error
311    /// occurred.
312    #[corresponds(EVP_DigestVerifyFinal)]
313    #[inline]
314    pub fn digest_verify_final(&mut self, signature: &[u8]) -> Result<bool, ErrorStack> {
315        unsafe {
316            let r = ffi::EVP_DigestVerifyFinal(
317                self.as_ptr(),
318                signature.as_ptr() as *mut _,
319                signature.len(),
320            );
321            if r == 1 {
322                Ok(true)
323            } else {
324                let errors = ErrorStack::get();
325                if errors.errors().is_empty() {
326                    Ok(false)
327                } else {
328                    Err(errors)
329                }
330            }
331        }
332    }
333
334    /// Computes the signature of the data in `from`.
335    ///
336    /// If `to` is set to `None`, an upper bound on the number of bytes required for the output buffer will be
337    /// returned.
338    ///
339    /// Requires OpenSSL 1.1.1 or newer.
340    #[corresponds(EVP_DigestSign)]
341    #[cfg(any(ossl111, boringssl, awslc, libressl))]
342    #[inline]
343    pub fn digest_sign(&mut self, from: &[u8], to: Option<&mut [u8]>) -> Result<usize, ErrorStack> {
344        let mut len = to.as_ref().map_or(0, |b| b.len());
345
346        unsafe {
347            cvt(ffi::EVP_DigestSign(
348                self.as_ptr(),
349                to.map_or(ptr::null_mut(), |b| b.as_mut_ptr()),
350                &mut len,
351                from.as_ptr(),
352                from.len(),
353            ))?;
354        }
355
356        Ok(len)
357    }
358
359    /// Like [`Self::digest_sign`] but appends the signature to a [`Vec`].
360    #[cfg(any(ossl111, boringssl, awslc, libressl))]
361    pub fn digest_sign_to_vec(
362        &mut self,
363        from: &[u8],
364        to: &mut Vec<u8>,
365    ) -> Result<usize, ErrorStack> {
366        let base = to.len();
367        let len = self.digest_sign(from, None)?;
368        to.resize(base + len, 0);
369        let len = self.digest_sign(from, Some(&mut to[base..]))?;
370        to.truncate(base + len);
371        Ok(len)
372    }
373
374    /// Verifies the signature of the data in `data`.
375    ///
376    /// Returns `Ok(true)` if the signature is valid, `Ok(false)` if the signature is invalid, and `Err` if an error
377    /// occurred.
378    ///
379    /// Requires OpenSSL 1.1.1 or newer.
380    #[corresponds(EVP_DigestVerify)]
381    #[cfg(any(ossl111, boringssl, awslc, libressl))]
382    #[inline]
383    pub fn digest_verify(&mut self, data: &[u8], signature: &[u8]) -> Result<bool, ErrorStack> {
384        unsafe {
385            let r = cvt(ffi::EVP_DigestVerify(
386                self.as_ptr(),
387                signature.as_ptr(),
388                signature.len(),
389                data.as_ptr(),
390                data.len(),
391            ))?;
392            Ok(r == 1)
393        }
394    }
395
396    /// Returns the size of the message digest, i.e. the size of the hash
397    #[corresponds(EVP_MD_CTX_size)]
398    #[inline]
399    pub fn size(&self) -> usize {
400        unsafe { ffi::EVP_MD_CTX_size(self.as_ptr()) as usize }
401    }
402
403    /// Resets the underlying EVP_MD_CTX instance
404    #[corresponds(EVP_MD_CTX_reset)]
405    #[cfg(any(ossl111, boringssl, awslc, libressl))]
406    #[inline]
407    pub fn reset(&mut self) -> Result<(), ErrorStack> {
408        unsafe {
409            let _ = cvt(ffi::EVP_MD_CTX_reset(self.as_ptr()))?;
410            Ok(())
411        }
412    }
413}
414
415#[cfg(test)]
416mod test {
417    use super::*;
418    use crate::md::Md;
419    use crate::pkey::PKey;
420    use crate::rsa::Rsa;
421
422    #[test]
423    fn verify_fail() {
424        let key1 = Rsa::generate(4096).unwrap();
425        let key1 = PKey::from_rsa(key1).unwrap();
426
427        let md = Md::sha256();
428        let data = b"Some Crypto Text";
429
430        let mut ctx = MdCtx::new().unwrap();
431        ctx.digest_sign_init(Some(md), &key1).unwrap();
432        ctx.digest_sign_update(data).unwrap();
433        let mut signature = vec![];
434        ctx.digest_sign_final_to_vec(&mut signature).unwrap();
435
436        let bad_data = b"Some Crypto text";
437
438        ctx.digest_verify_init(Some(md), &key1).unwrap();
439        ctx.digest_verify_update(bad_data).unwrap();
440        assert!(matches!(
441            ctx.digest_verify_final(&signature),
442            Ok(false) | Err(_)
443        ));
444        assert!(ErrorStack::get().errors().is_empty());
445    }
446
447    #[test]
448    fn verify_success() {
449        let key1 = Rsa::generate(2048).unwrap();
450        let key1 = PKey::from_rsa(key1).unwrap();
451
452        let md = Md::sha256();
453        let data = b"Some Crypto Text";
454
455        let mut ctx = MdCtx::new().unwrap();
456        ctx.digest_sign_init(Some(md), &key1).unwrap();
457        ctx.digest_sign_update(data).unwrap();
458        let mut signature = vec![];
459        ctx.digest_sign_final_to_vec(&mut signature).unwrap();
460
461        let good_data = b"Some Crypto Text";
462
463        ctx.digest_verify_init(Some(md), &key1).unwrap();
464        ctx.digest_verify_update(good_data).unwrap();
465        let valid = ctx.digest_verify_final(&signature).unwrap();
466        assert!(valid);
467    }
468
469    #[test]
470    fn verify_with_public_success() {
471        let rsa = Rsa::generate(2048).unwrap();
472        let key1 = PKey::from_rsa(rsa.clone()).unwrap();
473
474        let md = Md::sha256();
475        let data = b"Some Crypto Text";
476
477        let mut ctx = MdCtx::new().unwrap();
478        ctx.digest_sign_init(Some(md), &key1).unwrap();
479        ctx.digest_sign_update(data).unwrap();
480        let mut signature = vec![];
481        ctx.digest_sign_final_to_vec(&mut signature).unwrap();
482
483        let good_data = b"Some Crypto Text";
484
485        // try to verify using only public components of the key
486        let n = rsa.n().to_owned().unwrap();
487        let e = rsa.e().to_owned().unwrap();
488
489        let rsa = Rsa::from_public_components(n, e).unwrap();
490        let key1 = PKey::from_rsa(rsa).unwrap();
491
492        ctx.digest_verify_init(Some(md), &key1).unwrap();
493        ctx.digest_verify_update(good_data).unwrap();
494        let valid = ctx.digest_verify_final(&signature).unwrap();
495        assert!(valid);
496    }
497
498    #[test]
499    fn verify_md_ctx_size() {
500        let mut ctx = MdCtx::new().unwrap();
501        ctx.digest_init(Md::sha224()).unwrap();
502        assert_eq!(Md::sha224().size(), ctx.size());
503        assert_eq!(Md::sha224().size(), 28);
504
505        let mut ctx = MdCtx::new().unwrap();
506        ctx.digest_init(Md::sha256()).unwrap();
507        assert_eq!(Md::sha256().size(), ctx.size());
508        assert_eq!(Md::sha256().size(), 32);
509
510        let mut ctx = MdCtx::new().unwrap();
511        ctx.digest_init(Md::sha384()).unwrap();
512        assert_eq!(Md::sha384().size(), ctx.size());
513        assert_eq!(Md::sha384().size(), 48);
514
515        let mut ctx = MdCtx::new().unwrap();
516        ctx.digest_init(Md::sha512()).unwrap();
517        assert_eq!(Md::sha512().size(), ctx.size());
518        assert_eq!(Md::sha512().size(), 64);
519    }
520
521    #[test]
522    #[cfg(any(ossl111, boringssl, awslc, libressl))]
523    fn verify_md_ctx_reset() {
524        let hello_expected =
525            hex::decode("185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969")
526                .unwrap();
527        let world_expected =
528            hex::decode("78ae647dc5544d227130a0682a51e30bc7777fbb6d8a8f17007463a3ecd1d524")
529                .unwrap();
530        // Calculate SHA-256 digest of "Hello"
531        let mut ctx = MdCtx::new().unwrap();
532        ctx.digest_init(Md::sha256()).unwrap();
533        ctx.digest_update(b"Hello").unwrap();
534        let mut result = vec![0; 32];
535        let result_len = ctx.digest_final(result.as_mut_slice()).unwrap();
536        assert_eq!(result_len, result.len());
537        // Validate result of "Hello"
538        assert_eq!(result, hello_expected);
539
540        // Create new context
541        let mut ctx = MdCtx::new().unwrap();
542        // Initialize and update to "Hello"
543        ctx.digest_init(Md::sha256()).unwrap();
544        ctx.digest_update(b"Hello").unwrap();
545        // Now reset, init to SHA-256 and use "World"
546        ctx.reset().unwrap();
547        ctx.digest_init(Md::sha256()).unwrap();
548        ctx.digest_update(b"World").unwrap();
549
550        let mut reset_result = vec![0; 32];
551        let result_len = ctx.digest_final(reset_result.as_mut_slice()).unwrap();
552        assert_eq!(result_len, reset_result.len());
553        // Validate result of digest of "World"
554        assert_eq!(reset_result, world_expected);
555    }
556
557    #[test]
558    fn digest_final_checks_length() {
559        let mut ctx = MdCtx::new().unwrap();
560        ctx.digest_init(Md::sha256()).unwrap();
561        ctx.digest_update(b"Some Crypto Text").unwrap();
562        let mut digest = [0; 16];
563        assert!(ctx.digest_final(&mut digest).is_err());
564    }
565}