JakeDern commented on issue #413: URL: https://github.com/apache/arrow-rs-object-store/issues/413#issuecomment-3141808890
I had also started poking around with an implementation. Mine is a little different from yours @faysou in that I was trying to avoid mutually exclusive features because my understanding is that those can cause problems if two different dependencies use them differently. The idea being the crate uses ring by default, but if some feature like `open-ssl` is enabled then it uses the `openssl` crate instead. I also pulled everything out to a crypto module and tried to encapsulate it behind a trait. Here's as far as I got: ```rust use std::ops::Deref; /// Error type for crypto operations /// TODO: Figure out a better pattern here #[derive(Debug)] pub(crate) enum CryptoError { #[cfg(feature = "open-ssl")] OpenSsl(openssl::error::ErrorStack), } impl std::fmt::Display for CryptoError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { #[cfg(feature = "open-ssl")] CryptoError::OpenSsl(e) => write!(f, "OpenSSL error: {}", e), } } } impl std::error::Error for CryptoError {} #[cfg(feature = "open-ssl")] impl From<openssl::error::ErrorStack> for CryptoError { fn from(e: openssl::error::ErrorStack) -> Self { CryptoError::OpenSsl(e) } } /// Wrapper type for digests that abstracts over different crypto providers' types, some of which don't allocate pub(crate) struct Digest<T> { inner: T, } impl<T> Digest<T> { fn new(inner: T) -> Self { Self { inner } } } impl<T: AsRef<[u8]>> Deref for Digest<T> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.inner.as_ref() } } /// Wrapper type for HMAC tags that abstracts over different crypto providers' types, some of which don't allocate pub(crate) struct Tag<T> { inner: T, } impl<T> Tag<T> { fn new(inner: T) -> Self { Self { inner } } } impl<T: AsRef<[u8]>> Deref for Tag<T> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.inner.as_ref() } } pub trait Crypto { type DigestType: AsRef<[u8]>; type TagType: AsRef<[u8]>; fn digest_sha256(bytes: &[u8]) -> Result<Digest<Self::DigestType>, CryptoError>; fn hmac_sha256(secret: &[u8], bytes: &[u8]) -> Result<Tag<Self::TagType>, CryptoError>; } // Type alias to the current crypto provider based on feature flags #[cfg(feature = "open-ssl")] type CryptoProvider = openssl_crypto::OpenSslCrypto; #[cfg(not(feature = "open-ssl"))] type CryptoProvider = ring_crypto::RingCrypto; // Type aliases for the associated types of the current provider // Note: This seems to be necessary or we get ambiguous type errors on the public functions type CurrentDigest = <CryptoProvider as Crypto>::DigestType; type CurrentTag = <CryptoProvider as Crypto>::TagType; pub(crate) fn digest_sha256(bytes: &[u8]) -> Result<Digest<CurrentDigest>, CryptoError> { CryptoProvider::digest_sha256(bytes) } pub(crate) fn hmac_sha256(secret: &[u8], bytes: &[u8]) -> Result<Tag<CurrentTag>, CryptoError> { CryptoProvider::hmac_sha256(secret, bytes) } pub(crate) fn hex_digest(bytes: &[u8]) -> Result<String, CryptoError> { let sha_256_hash = digest_sha256(bytes)?; Ok(hex_encode(&sha_256_hash)) } pub(crate) fn hex_encode(bytes: &[u8]) -> String { use std::fmt::Write; let mut out = String::with_capacity(bytes.len() * 2); for byte in bytes { // String writing is infallible let _ = write!(out, "{byte:02x}"); } out } #[cfg(feature = "open-ssl")] mod openssl_crypto { use openssl::hash::{DigestBytes, MessageDigest}; use openssl::pkey::PKey; use openssl::sign::Signer; use super::{Crypto, CryptoError, Digest, Tag}; pub struct OpenSslCrypto; impl Crypto for OpenSslCrypto { type DigestType = DigestBytes; type TagType = Vec<u8>; fn digest_sha256(bytes: &[u8]) -> Result<Digest<Self::DigestType>, CryptoError> { let digest = openssl::hash::hash(MessageDigest::sha256(), bytes)?; Ok(Digest::new(digest)) } fn hmac_sha256(secret: &[u8], bytes: &[u8]) -> Result<Tag<Self::TagType>, CryptoError> { let key = PKey::hmac(secret)?; let mut signer = Signer::new(MessageDigest::sha256(), &key)?; signer.update(bytes)?; let hmac = signer.sign_to_vec()?; Ok(Tag::new(hmac)) } } } #[cfg(not(feature = "open-ssl"))] mod ring_crypto { use super::{Crypto, CryptoError, Digest, Tag}; pub struct RingCrypto; impl Crypto for RingCrypto { type DigestType = ring::digest::Digest; type TagType = ring::hmac::Tag; fn digest_sha256(bytes: &[u8]) -> Result<Digest<Self::DigestType>, CryptoError> { let digest = ring::digest::digest(&ring::digest::SHA256, bytes); Ok(Digest::new(digest)) } fn hmac_sha256(secret: &[u8], bytes: &[u8]) -> Result<Tag<Self::TagType>, CryptoError> { let key = ring::hmac::Key::new(ring::hmac::HMAC_SHA256, secret); let tag = ring::hmac::sign(&key, bytes); Ok(Tag::new(tag)) } } } ``` I'm happy to clean this up as well, I just need to write some tests and also move the code in the crate to use this instead. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: github-unsubscr...@arrow.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org