aboutsummaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
authorAdam Langley <agl@chromium.org>2024-01-05 15:50:16 -0800
committerBoringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-01-13 14:26:18 +0000
commit677414b2f2bbcb2fff5744aa1dbbec555f1f8672 (patch)
tree20255048bbc26eb143adf88b6fc1721bf3b7d258 /rust
parentec6a4055437bd384a1dff3842c084eec49e203a8 (diff)
downloadboringssl-677414b2f2bbcb2fff5744aa1dbbec555f1f8672.zip
boringssl-677414b2f2bbcb2fff5744aa1dbbec555f1f8672.tar.gz
boringssl-677414b2f2bbcb2fff5744aa1dbbec555f1f8672.tar.bz2
Reworking bssl_crypto: digest
Change-Id: I43de22995908ea39b19aa03d167c62a9580ba7b1 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/65168 Reviewed-by: Bob Beck <bbe@google.com> Commit-Queue: Adam Langley <agl@google.com> Reviewed-by: Maurice Lam <yukl@google.com>
Diffstat (limited to 'rust')
-rw-r--r--rust/bssl-crypto/src/digest.rs284
-rw-r--r--rust/bssl-crypto/src/hkdf.rs13
-rw-r--r--rust/bssl-crypto/src/hmac.rs25
-rw-r--r--rust/bssl-crypto/src/lib.rs23
-rw-r--r--rust/bssl-crypto/src/macros.rs105
5 files changed, 290 insertions, 160 deletions
diff --git a/rust/bssl-crypto/src/digest.rs b/rust/bssl-crypto/src/digest.rs
index 7240297..a10b5ab 100644
--- a/rust/bssl-crypto/src/digest.rs
+++ b/rust/bssl-crypto/src/digest.rs
@@ -13,162 +13,135 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-use core::marker::PhantomData;
+//! Hash functions.
+//!
+//! ```
+//! use bssl_crypto::digest;
+//!
+//! // One-shot hashing.
+//! let digest: [u8; 32] = digest::Sha256::hash(b"hello");
+//!
+//! // Incremental hashing.
+//! let mut ctx = digest::Sha256::new();
+//! ctx.update(b"hel");
+//! ctx.update(b"lo");
+//! let digest2: [u8; 32] = ctx.digest();
+//!
+//! assert_eq!(digest, digest2);
+//! ```
+
+use crate::{sealed, FfiSlice, ForeignTypeRef};
+use bssl_sys;
-use crate::{CSlice, ForeignTypeRef};
-
-/// The SHA-256 digest algorithm.
-#[derive(Clone)]
-pub struct Sha256 {}
-
-/// The SHA-512 digest algorithm.
-#[derive(Clone)]
-pub struct Sha512 {}
-
-/// A reference to an [`Md`], which abstracts the details of a specific hash function allowing code
-/// to deal with the concept of a "hash function" without needing to know exactly which hash function
-/// it is.
#[non_exhaustive]
+#[doc(hidden)]
pub struct MdRef;
unsafe impl ForeignTypeRef for MdRef {
type CType = bssl_sys::EVP_MD;
}
-/// Used internally to get a BoringSSL internal MD
-pub trait Md {
- /// The output size of the hash operation.
- const OUTPUT_SIZE: usize;
+/// Used internally to parameterize other primitives.
+pub trait Algorithm {
+ /// The size of the resulting digest.
+ const OUTPUT_LEN: usize;
/// Gets a reference to a message digest algorithm to be used by the HKDF implementation.
- fn get_md() -> &'static MdRef;
+ #[doc(hidden)]
+ fn get_md(_: sealed::Sealed) -> &'static MdRef;
}
-impl Md for Sha256 {
- const OUTPUT_SIZE: usize = bssl_sys::SHA256_DIGEST_LENGTH as usize;
-
- fn get_md() -> &'static MdRef {
- // Safety:
- // - this always returns a valid pointer to an EVP_MD
- unsafe { MdRef::from_ptr(bssl_sys::EVP_sha256() as *mut _) }
- }
+/// The insecure SHA-1 hash algorithm.
+///
+/// Some existing protocols depend on SHA-1 and so it is provided here, but it
+/// does not provide collision resistance and should not be used if at all
+/// avoidable. Use SHA-256 instead.
+#[derive(Clone)]
+pub struct InsecureSha1 {
+ ctx: bssl_sys::SHA_CTX,
}
-impl Sha256 {
- /// Creates a new [Digest] to compute a SHA-256 hash.
- pub fn new_digest() -> Digest<Self, { Self::OUTPUT_SIZE }> {
- // Note: This cannot be in the trait because using associated constants exprs there
- // requires nightly.
- Digest::<Self, { Self::OUTPUT_SIZE }>::new()
- }
+unsafe_iuf_algo!(
+ InsecureSha1,
+ 20,
+ EVP_sha1,
+ SHA1,
+ SHA1_Init,
+ SHA1_Update,
+ SHA1_Final
+);
+
+/// The SHA-256 hash algorithm.
+#[derive(Clone)]
+pub struct Sha256 {
+ ctx: bssl_sys::SHA256_CTX,
}
-impl Md for Sha512 {
- const OUTPUT_SIZE: usize = bssl_sys::SHA512_DIGEST_LENGTH as usize;
-
- fn get_md() -> &'static MdRef {
- // Safety:
- // - this always returns a valid pointer to an EVP_MD
- unsafe { MdRef::from_ptr(bssl_sys::EVP_sha512() as *mut _) }
- }
+unsafe_iuf_algo!(
+ Sha256,
+ 32,
+ EVP_sha256,
+ SHA256,
+ SHA256_Init,
+ SHA256_Update,
+ SHA256_Final
+);
+
+/// The SHA-384 hash algorithm.
+#[derive(Clone)]
+pub struct Sha384 {
+ ctx: bssl_sys::SHA512_CTX,
}
-impl Sha512 {
- /// Create a new [Digest] to compute a SHA-512 hash.
- pub fn new_digest() -> Digest<Self, { Self::OUTPUT_SIZE }> {
- // Note: This cannot be in the trait because using associated constants exprs there
- // requires nightly.
- Digest::<Self, { Self::OUTPUT_SIZE }>::new()
- }
+unsafe_iuf_algo!(
+ Sha384,
+ 48,
+ EVP_sha384,
+ SHA384,
+ SHA384_Init,
+ SHA384_Update,
+ SHA384_Final
+);
+
+/// The SHA-512 hash algorithm.
+#[derive(Clone)]
+pub struct Sha512 {
+ ctx: bssl_sys::SHA512_CTX,
}
-/// A pending digest operation.
-pub struct Digest<M: Md, const OUTPUT_SIZE: usize>(bssl_sys::EVP_MD_CTX, PhantomData<M>);
-
-impl<M: Md, const OUTPUT_SIZE: usize> Digest<M, OUTPUT_SIZE> {
- /// Creates a new Digest from the given `Md` type parameter.
- ///
- /// Panics:
- /// - If `Md::OUTPUT_SIZE` is not the same as `OUTPUT_SIZE`.
- fn new() -> Self {
- // Note: runtime assertion needed here since using {M::OUTPUT_SIZE} in return type requires
- // unstable Rust feature.
- assert_eq!(M::OUTPUT_SIZE, OUTPUT_SIZE);
- let mut md_ctx_uninit = core::mem::MaybeUninit::<bssl_sys::EVP_MD_CTX>::uninit();
- // Safety:
- // - `EVP_DigestInit` initializes `md_ctx_uninit`
- // - `MdRef` ensures the validity of `md.as_ptr`
- let result =
- unsafe { bssl_sys::EVP_DigestInit(md_ctx_uninit.as_mut_ptr(), M::get_md().as_ptr()) };
- assert_eq!(result, 1, "bssl_sys::EVP_DigestInit failed");
- // Safety:
- // - md_ctx_uninit initialized with EVP_DigestInit, and the function returned 1 (success)
- let md_ctx = unsafe { md_ctx_uninit.assume_init() };
- Self(md_ctx, PhantomData)
- }
-
- /// Hashes the provided input into the current digest operation.
- pub fn update(&mut self, data: &[u8]) {
- let data_ffi = CSlice(data);
- // Safety:
- // - `data` is a CSlice from safe Rust.
- let result = unsafe {
- bssl_sys::EVP_DigestUpdate(&mut self.0, data_ffi.as_ptr() as *const _, data_ffi.len())
- };
- assert_eq!(result, 1, "bssl_sys::EVP_DigestUpdate failed");
- }
-
- /// Computes the final digest value, consuming the object.
- #[allow(clippy::expect_used)]
- pub fn finalize(mut self) -> [u8; OUTPUT_SIZE] {
- let mut digest_uninit =
- core::mem::MaybeUninit::<[u8; bssl_sys::EVP_MAX_MD_SIZE as usize]>::uninit();
- let mut len_uninit = core::mem::MaybeUninit::<u32>::uninit();
- // Safety:
- // - `digest_uninit` is allocated to `EVP_MAX_MD_SIZE` bytes long, as required by
- // EVP_DigestFinal_ex
- // - `self.0` is owned by `self`, and is going to be cleaned up on drop.
- let result = unsafe {
- bssl_sys::EVP_DigestFinal_ex(
- &mut self.0,
- digest_uninit.as_mut_ptr() as *mut _,
- len_uninit.as_mut_ptr(),
- )
- };
- assert_eq!(result, 1, "bssl_sys::EVP_DigestFinal_ex failed");
- // Safety:
- // - `len_uninit` is initialized by `EVP_DigestFinal_ex`, and we checked the result above
- let len = unsafe { len_uninit.assume_init() };
- assert_eq!(
- OUTPUT_SIZE, len as usize,
- "bssl_sys::EVP_DigestFinal_ex failed"
- );
- // Safety: Result of DigestFinal_ex was checked above
- let digest = unsafe { digest_uninit.assume_init() };
- digest
- .get(..OUTPUT_SIZE)
- .and_then(|digest| digest.try_into().ok())
- .expect("The length of `digest` was checked above")
- }
+unsafe_iuf_algo!(
+ Sha512,
+ 64,
+ EVP_sha512,
+ SHA512,
+ SHA512_Init,
+ SHA512_Update,
+ SHA512_Final
+);
+
+/// The SHA-512/256 hash algorithm.
+#[derive(Clone)]
+pub struct Sha512_256 {
+ ctx: bssl_sys::SHA512_CTX,
}
-impl<M: Md, const OUTPUT_SIZE: usize> Drop for Digest<M, OUTPUT_SIZE> {
- fn drop(&mut self) {
- // Safety: `self.0` is owned by `self`, and is invalidated after `drop`.
- unsafe {
- bssl_sys::EVP_MD_CTX_cleanup(&mut self.0);
- }
- }
-}
+unsafe_iuf_algo!(
+ Sha512_256,
+ 32,
+ EVP_sha512_256,
+ SHA512_256,
+ SHA512_256_Init,
+ SHA512_256_Update,
+ SHA512_256_Final
+);
#[cfg(test)]
mod test {
- use crate::test_helpers::decode_hex;
-
use super::*;
+ use crate::test_helpers::decode_hex;
#[test]
- fn test_sha256_c_type() {
+ fn sha256_c_type() {
unsafe {
assert_eq!(
MdRef::from_ptr(bssl_sys::EVP_sha256() as *mut _).as_ptr(),
@@ -178,7 +151,7 @@ mod test {
}
#[test]
- fn test_sha512_c_type() {
+ fn sha512_c_type() {
unsafe {
assert_eq!(
MdRef::from_ptr(bssl_sys::EVP_sha512() as *mut _).as_ptr(),
@@ -188,31 +161,60 @@ mod test {
}
#[test]
- fn test_digest_sha256() {
- let mut digest = Sha256::new_digest();
+ fn sha1() {
+ assert_eq!(
+ decode_hex("a9993e364706816aba3e25717850c26c9cd0d89d"),
+ InsecureSha1::hash(b"abc")
+ );
+ }
+
+ #[test]
+ fn sha256() {
let msg: [u8; 4] = decode_hex("74ba2521");
- digest.update(&msg);
let expected_digest: [u8; 32] =
decode_hex("b16aa56be3880d18cd41e68384cf1ec8c17680c45a02b1575dc1518923ae8b0e");
- assert_eq!(expected_digest, digest.finalize());
+
+ assert_eq!(Sha256::hash(&msg), expected_digest);
+
+ let mut ctx = Sha256::new();
+ ctx.update(&msg);
+ assert_eq!(expected_digest, ctx.digest());
+
+ let mut ctx = Sha256::new();
+ ctx.update(&msg[0..1]);
+ let mut ctx2 = ctx.clone();
+ ctx2.update(&msg[1..]);
+ assert_eq!(expected_digest, ctx2.digest());
+ }
+
+ #[test]
+ fn sha384() {
+ assert_eq!(
+ decode_hex("cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"),
+ Sha384::hash(b"abc")
+ );
}
#[test]
- fn test_digest_sha512() {
- let mut digest = Sha512::new_digest();
+ fn sha512() {
let msg: [u8; 4] = decode_hex("23be86d5");
- digest.update(&msg);
let expected_digest: [u8; 64] = decode_hex(concat!(
"76d42c8eadea35a69990c63a762f330614a4699977f058adb988f406fb0be8f2",
"ea3dce3a2bbd1d827b70b9b299ae6f9e5058ee97b50bd4922d6d37ddc761f8eb"
));
- assert_eq!(expected_digest, digest.finalize());
+
+ assert_eq!(Sha512::hash(&msg), expected_digest);
+
+ let mut ctx = Sha512::new();
+ ctx.update(&msg);
+ assert_eq!(expected_digest, ctx.digest());
}
#[test]
- #[should_panic]
- fn test_digest_wrong_size() {
- // This should not happen since we don't externally expose Digest::new
- Digest::<Sha256, 64>::new();
+ fn sha512_256() {
+ assert_eq!(
+ decode_hex("53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"),
+ Sha512_256::hash(b"abc")
+ );
}
}
diff --git a/rust/bssl-crypto/src/hkdf.rs b/rust/bssl-crypto/src/hkdf.rs
index e4e9c01..f3a68ee 100644
--- a/rust/bssl-crypto/src/hkdf.rs
+++ b/rust/bssl-crypto/src/hkdf.rs
@@ -12,8 +12,9 @@
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-use crate::digest::Md;
+use crate::digest;
use crate::digest::{Sha256, Sha512};
+use crate::sealed;
use crate::{CSlice, CSliceMut, ForeignTypeRef};
use alloc::vec::Vec;
use core::marker::PhantomData;
@@ -31,15 +32,15 @@ pub struct InvalidLength;
/// Implementation of HKDF operations which are generic over a provided hashing functions. Type
/// aliases are provided above for convenience of commonly used hashes
-pub struct Hkdf<M: Md> {
+pub struct Hkdf<MD: digest::Algorithm> {
salt: Option<Vec<u8>>,
ikm: Vec<u8>,
- _marker: PhantomData<M>,
+ _marker: PhantomData<MD>,
}
-impl<M: Md> Hkdf<M> {
+impl<MD: digest::Algorithm> Hkdf<MD> {
/// The max length of the output key material used for expanding
- pub const MAX_OUTPUT_LENGTH: usize = M::OUTPUT_SIZE * 255;
+ pub const MAX_OUTPUT_LENGTH: usize = MD::OUTPUT_LEN * 255;
/// Creates a new instance of HKDF from a salt and key material
pub fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self {
@@ -79,7 +80,7 @@ impl<M: Md> Hkdf<M> {
bssl_sys::HKDF(
okm_cslice.as_mut_ptr(),
okm_cslice.len(),
- M::get_md().as_ptr(),
+ MD::get_md(sealed::Sealed).as_ptr(),
CSlice::from(self.ikm.as_slice()).as_ptr(),
self.ikm.as_slice().len(),
CSlice::from(salt).as_ptr(),
diff --git a/rust/bssl-crypto/src/hmac.rs b/rust/bssl-crypto/src/hmac.rs
index 167e92e..56b06e6 100644
--- a/rust/bssl-crypto/src/hmac.rs
+++ b/rust/bssl-crypto/src/hmac.rs
@@ -13,8 +13,9 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
use crate::{
- digest::{Md, Sha256, Sha512},
- CSlice, ForeignTypeRef as _,
+ digest,
+ digest::{Sha256, Sha512},
+ sealed, CSlice, ForeignTypeRef as _,
};
use core::{
ffi::{c_uint, c_void},
@@ -154,11 +155,11 @@ pub struct MacError;
/// Private generically implemented function for computing hmac as a oneshot operation.
/// This should only be exposed publicly by types with the correct output size `N` which corresponds
-/// to the output size of the provided generic hash function. Ideally `N` would just come from `M`,
+/// to the output size of the provided generic hash function. Ideally `N` would just come from `MD`,
/// but this is not possible until the Rust language can support the `min_const_generics` feature.
/// Until then we will have to pass both separately: https://github.com/rust-lang/rust/issues/60551
#[inline]
-fn hmac<const N: usize, M: Md>(key: &[u8], data: &[u8]) -> [u8; N] {
+fn hmac<const N: usize, MD: digest::Algorithm>(key: &[u8], data: &[u8]) -> [u8; N] {
let mut out = [0_u8; N];
let mut size: c_uint = 0;
@@ -167,7 +168,7 @@ fn hmac<const N: usize, M: Md>(key: &[u8], data: &[u8]) -> [u8; N] {
// - If NULL is returned on error we panic immediately
let result = unsafe {
bssl_sys::HMAC(
- M::get_md().as_ptr(),
+ MD::get_md(sealed::Sealed).as_ptr(),
CSlice::from(key).as_ptr(),
key.len(),
CSlice::from(data).as_ptr(),
@@ -184,15 +185,15 @@ fn hmac<const N: usize, M: Md>(key: &[u8], data: &[u8]) -> [u8; N] {
/// Private generically implemented hmac instance given a generic hash function and a length `N`,
/// where `N` is the output size of the hash function. This should only be exposed publicly by
/// wrapper types with the correct output size `N` which corresponds to the output size of the
-/// provided generic hash function. Ideally `N` would just come from `M`, but this is not possible
+/// provided generic hash function. Ideally `N` would just come from `MD`, but this is not possible
/// until the Rust language can support the `min_const_generics` feature. Until then we will have to
/// pass both separately: https://github.com/rust-lang/rust/issues/60551
-struct Hmac<const N: usize, M: Md> {
+struct Hmac<const N: usize, MD: digest::Algorithm> {
ctx: *mut bssl_sys::HMAC_CTX,
- _marker: PhantomData<M>,
+ _marker: PhantomData<MD>,
}
-impl<const N: usize, M: Md> Hmac<N, M> {
+impl<const N: usize, MD: digest::Algorithm> Hmac<N, MD> {
/// Creates a new HMAC operation from a fixed-length key.
fn new(key: [u8; N]) -> Self {
Self::new_from_slice(&key)
@@ -219,7 +220,7 @@ impl<const N: usize, M: Md> Hmac<N, M> {
ctx,
CSlice::from(key).as_ptr() as *const c_void,
key.len(),
- M::get_md().as_ptr(),
+ MD::get_md(sealed::Sealed).as_ptr(),
ptr::null_mut(),
)
};
@@ -311,7 +312,7 @@ impl<const N: usize, M: Md> Hmac<N, M> {
self.ctx,
ptr::null_mut(),
0,
- M::get_md().as_ptr(),
+ MD::get_md(sealed::Sealed).as_ptr(),
ptr::null_mut(),
)
};
@@ -319,7 +320,7 @@ impl<const N: usize, M: Md> Hmac<N, M> {
}
}
-impl<const N: usize, M: Md> Drop for Hmac<N, M> {
+impl<const N: usize, MD: digest::Algorithm> Drop for Hmac<N, MD> {
fn drop(&mut self) {
unsafe { bssl_sys::HMAC_CTX_free(self.ctx) }
}
diff --git a/rust/bssl-crypto/src/lib.rs b/rust/bssl-crypto/src/lib.rs
index 022f5a3..dea522f 100644
--- a/rust/bssl-crypto/src/lib.rs
+++ b/rust/bssl-crypto/src/lib.rs
@@ -30,6 +30,9 @@ extern crate core;
use core::ffi::c_void;
+#[macro_use]
+mod macros;
+
/// Authenticated Encryption with Additional Data algorithms.
pub mod aead;
@@ -39,7 +42,6 @@ pub mod aes;
/// Ciphers.
pub mod cipher;
-/// Hash functions.
pub mod digest;
/// Ed25519, a signature scheme.
@@ -247,6 +249,20 @@ unsafe trait ForeignType {
fn as_ptr(&self) -> *mut Self::CType;
}
+/// Returns a BoringSSL structure that is initialized by some function.
+/// Requires that the given function completely initializes the value.
+///
+/// (Tagged `unsafe` because a no-op argument would otherwise expose
+/// uninitialized memory.)
+unsafe fn initialized_struct<T, F>(init: F) -> T
+where
+ F: FnOnce(*mut T),
+{
+ let mut out_uninit = core::mem::MaybeUninit::<T>::uninit();
+ init(out_uninit.as_mut_ptr());
+ unsafe { out_uninit.assume_init() }
+}
+
/// Wrap a closure that initializes an output buffer and return that buffer as
/// an array. Requires that the closure fully initialize the given buffer.
///
@@ -290,3 +306,8 @@ where
None
}
}
+
+/// Used to prevent external implementations of internal traits.
+mod sealed {
+ pub struct Sealed;
+}
diff --git a/rust/bssl-crypto/src/macros.rs b/rust/bssl-crypto/src/macros.rs
new file mode 100644
index 0000000..79049d4
--- /dev/null
+++ b/rust/bssl-crypto/src/macros.rs
@@ -0,0 +1,105 @@
+/* Copyright (c) 2024, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+// Generates a hash function from init/update/final-style FFI functions. Rust
+// doesn't accept function pointers as a generic arguments so this is the only
+// mechanism to avoid duplicating the code.
+//
+// The name is prefixed with "unsafe_" because it contains unsafe blocks.
+//
+// Safety: see the "Safety" sections within about the requirements for the
+// functions named in the macro parameters.
+macro_rules! unsafe_iuf_algo {
+ ($name:ident, $output_len:expr, $evp_md:ident, $one_shot:ident, $init:ident, $update:ident, $final_func:ident) => {
+ impl Algorithm for $name {
+ const OUTPUT_LEN: usize = $output_len as usize;
+
+ fn get_md(_: sealed::Sealed) -> &'static MdRef {
+ // Safety:
+ // - this always returns a valid pointer to an EVP_MD.
+ unsafe { MdRef::from_ptr(bssl_sys::$evp_md() as *mut _) }
+ }
+ }
+
+ impl $name {
+ /// Digest `input` in a single operation.
+ pub fn hash(input: &[u8]) -> [u8; $output_len] {
+ // Safety: it is assumed that `$one_shot` indeed writes
+ // `$output_len` bytes.
+ unsafe {
+ crate::with_output_array(|out, _| {
+ bssl_sys::$one_shot(input.as_ffi_ptr(), input.len(), out);
+ })
+ }
+ }
+
+ /// Create a new context for incremental hashing.
+ pub fn new() -> Self {
+ unsafe {
+ Self {
+ ctx: crate::initialized_struct(|ctx| {
+ // Safety: type checking will ensure that `ctx` is the
+ // correct type for `$init` to write into.
+ bssl_sys::$init(ctx);
+ }),
+ }
+ }
+ }
+
+ /// Hash the contents of `input`.
+ pub fn update(&mut self, input: &[u8]) {
+ // Safety: arguments point to a valid buffer.
+ unsafe {
+ bssl_sys::$update(&mut self.ctx, input.as_ffi_void_ptr(), input.len());
+ }
+ }
+
+ /// Finish the hashing and return the digest.
+ pub fn digest(mut self) -> [u8; $output_len] {
+ // Safety: it is assumed that `$final_func` indeed writes
+ // `$output_len` bytes.
+ unsafe {
+ crate::with_output_array(|out, _| {
+ bssl_sys::$final_func(out, &mut self.ctx);
+ })
+ }
+ }
+ }
+
+ impl From<$name> for [u8; $output_len] {
+ fn from(ctx: $name) -> [u8; $output_len] {
+ ctx.digest()
+ }
+ }
+
+ impl From<$name> for alloc::vec::Vec<u8> {
+ fn from(ctx: $name) -> alloc::vec::Vec<u8> {
+ ctx.digest().into()
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::io::Write for $name {
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ self.update(buf);
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ Ok(())
+ }
+ }
+ };
+}