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

Reply via email to