diff options
author | Maurice Lam <yukl@google.com> | 2023-06-01 02:04:33 +0000 |
---|---|---|
committer | Boringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-06-05 18:03:39 +0000 |
commit | 4a0393fcf37d7dbd090a5bb2293601a9ec7605da (patch) | |
tree | 87fc36fd1f832f06ee47cc91e0b82f32555f93a6 /rust | |
parent | b0341041b03ea71d8371a9692aedae263fc06ee9 (diff) | |
download | boringssl-4a0393fcf37d7dbd090a5bb2293601a9ec7605da.zip boringssl-4a0393fcf37d7dbd090a5bb2293601a9ec7605da.tar.gz boringssl-4a0393fcf37d7dbd090a5bb2293601a9ec7605da.tar.bz2 |
Add SHA256 and SHA512 bindings to bssl-crypto
Bug: 285222360
Change-Id: I7f35bcc734dd853e99cb691bdc681f75c9f137e4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60265
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
Diffstat (limited to 'rust')
-rw-r--r-- | rust/bssl-crypto/src/digest.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/rust/bssl-crypto/src/digest.rs b/rust/bssl-crypto/src/digest.rs index 4f19419..ee26f3e 100644 --- a/rust/bssl-crypto/src/digest.rs +++ b/rust/bssl-crypto/src/digest.rs @@ -13,6 +13,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +use core::marker::PhantomData; + use crate::ForeignTypeRef; /// The BoringSSL implemented SHA-256 digest algorithm. @@ -26,6 +28,7 @@ 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] pub struct MdRef; unsafe impl ForeignTypeRef for MdRef { @@ -51,6 +54,15 @@ impl Md for Sha256 { } } +impl Sha256 { + /// Create a new [digest] to compute the SHA256 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() + } +} + impl Md for Sha512 { const OUTPUT_SIZE: usize = bssl_sys::SHA512_DIGEST_LENGTH as usize; @@ -61,8 +73,98 @@ impl Md for Sha512 { } } +impl Sha512 { + /// Create a new [digest] to compute the SHA512 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() + } +} + +/// A struct for computing the digest +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> { + + /// Create 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) + } + + /// Updates this digest computation using the given `data`. + pub fn update(&mut self, data: &[u8]) { + // Safety: + // - `data` is a slice from safe Rust. + let result = unsafe { + bssl_sys::EVP_DigestUpdate(&mut self.0, data.as_ptr() as *const _, data.len()) + }; + assert_eq!(result, 1, "bssl_sys::EVP_DigestUpdate failed"); + } + + /// Consumes this digest and returns the output digest value. + #[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") + } +} + +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); + } + } +} + #[cfg(test)] mod test { + use crate::test_helpers::decode_hex; + use super::*; #[test] @@ -84,4 +186,33 @@ mod test { ) } } + + #[test] + fn test_digest_sha256() { + let mut digest = Sha256::new_digest(); + let msg: [u8; 4] = decode_hex("74ba2521"); + digest.update(&msg); + let expected_digest: [u8; 32] = + decode_hex("b16aa56be3880d18cd41e68384cf1ec8c17680c45a02b1575dc1518923ae8b0e"); + assert_eq!(expected_digest, digest.finalize()); + } + + #[test] + fn test_digest_sha512() { + let mut digest = Sha512::new_digest(); + 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()); + } + + #[test] + #[should_panic] + fn test_digest_wrong_size() { + // This should not happen since we don't externally expose Digest::new + Digest::<Sha256, 64>::new(); + } } |