summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/cng
diff options
context:
space:
mode:
authorIgor Matuszewski <igor@sequoia-pgp.org>2020-04-10 17:49:50 +0200
committerIgor Matuszewski <igor@sequoia-pgp.org>2020-08-13 13:15:03 +0200
commitd8e09d5294ea14c540741d5cce63c1b9d704fe60 (patch)
tree04f91d5ff4985d9e7eaadaa860ad0b2fa3953c72 /openpgp/src/crypto/backend/cng
parent38fc08906e0152085af7f9b35a2038f317be087e (diff)
openpgp: Implement hashing using Windows CNG backend
Diffstat (limited to 'openpgp/src/crypto/backend/cng')
-rw-r--r--openpgp/src/crypto/backend/cng/hash.rs82
1 files changed, 82 insertions, 0 deletions
diff --git a/openpgp/src/crypto/backend/cng/hash.rs b/openpgp/src/crypto/backend/cng/hash.rs
new file mode 100644
index 00000000..33eeaeb9
--- /dev/null
+++ b/openpgp/src/crypto/backend/cng/hash.rs
@@ -0,0 +1,82 @@
+use core::convert::TryFrom;
+
+use crate::crypto::hash::Digest;
+use crate::types::HashAlgorithm;
+use crate::{Error, Result};
+
+use win_crypto_ng::hash as cng;
+
+impl Digest for cng::Hash {
+ fn digest_size(&self) -> usize {
+ self.hash_size().expect("CNG to not fail internally")
+ }
+
+ fn update(&mut self, data: &[u8]) {
+ let _ = self.hash(data);
+ }
+
+ fn digest(&mut self, digest: &mut [u8]) {
+ // TODO: Replace with CNG reusable hash objects, supported from Windows 8
+ // This would allow us to not re-create the CNG hash object each time we
+ // want to finish digest calculation
+ let algorithm = self.hash_algorithm()
+ .expect("CNG hash object to know its algorithm");
+ let new = cng::HashAlgorithm::open(algorithm)
+ .expect("CNG to open a new correct hash provider")
+ .new_hash()
+ .expect("Failed to create a new CNG hash object");
+
+ let old = std::mem::replace(self, new);
+ let buffer = old.finish()
+ .expect("CNG to not fail internally");
+
+ digest.copy_from_slice(&buffer.as_slice()[..digest.len()]);
+ }
+}
+
+impl TryFrom<HashAlgorithm> for cng::HashAlgorithmId {
+ type Error = Error;
+
+ fn try_from(value: HashAlgorithm) -> std::result::Result<Self, Self::Error> {
+ Ok(match value {
+ HashAlgorithm::SHA1 => cng::HashAlgorithmId::Sha1,
+ HashAlgorithm::SHA256 => cng::HashAlgorithmId::Sha256,
+ HashAlgorithm::SHA384 => cng::HashAlgorithmId::Sha384,
+ HashAlgorithm::SHA512 => cng::HashAlgorithmId::Sha512,
+ HashAlgorithm::MD5 => cng::HashAlgorithmId::Md5,
+ algo => Err(Error::UnsupportedHashAlgorithm(algo))?,
+ })
+ }
+}
+
+impl HashAlgorithm {
+ /// Whether Sequoia supports this algorithm.
+ pub fn is_supported(self) -> bool {
+ match self {
+ HashAlgorithm::SHA1 => true,
+ HashAlgorithm::SHA256 => true,
+ HashAlgorithm::SHA384 => true,
+ HashAlgorithm::SHA512 => true,
+ HashAlgorithm::MD5 => true,
+ _ => false,
+ }
+ }
+
+ /// Creates a new hash context for this algorithm.
+ ///
+ /// # Errors
+ ///
+ /// Fails with `Error::UnsupportedHashAlgorithm` if the selected crypto
+ /// backend does not support this algorithm. See
+ /// [`HashAlgorithm::is_supported`].
+ ///
+ /// [`HashAlgorithm::is_supported`]: #method.is_supported
+ pub(crate) fn new_hasher(self) -> Result<Box<dyn Digest>> {
+ let algo = cng::HashAlgorithmId::try_from(self)?;
+ let algo = cng::HashAlgorithm::open(algo)?;
+
+ Ok(Box::new(algo.new_hash().expect(
+ "CNG to always create a hasher object for valid algo",
+ )))
+ }
+}