1#![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 pub struct MdCtxRef;
110}
111
112impl MdCtx {
113 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 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 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 assert_eq!(result, hello_expected);
539
540 let mut ctx = MdCtx::new().unwrap();
542 ctx.digest_init(Md::sha256()).unwrap();
544 ctx.digest_update(b"Hello").unwrap();
545 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 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}