diff options
author | Igor Matuszewski <igor@sequoia-pgp.org> | 2020-04-10 01:39:20 +0200 |
---|---|---|
committer | Igor Matuszewski <igor@sequoia-pgp.org> | 2020-06-22 11:57:07 +0200 |
commit | 61c66c3df5b12f79102eccddf479f58c1a3bdd63 (patch) | |
tree | 226e4dfa536e2c78a9e66a616831deb34e91b054 | |
parent | c8c207e931f204e5746eaff7dd57382048d26492 (diff) |
openpgp: Move Nettle hashing impls to the backend module
-rw-r--r-- | Cargo.lock | 7 | ||||
-rw-r--r-- | openpgp/Cargo.toml | 1 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/nettle.rs | 1 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/nettle/hash.rs | 84 | ||||
-rw-r--r-- | openpgp/src/crypto/hash.rs | 124 |
5 files changed, 143 insertions, 74 deletions
@@ -516,6 +516,11 @@ dependencies = [ ] [[package]] +name = "dyn-clone" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1747,6 +1752,7 @@ dependencies = [ "base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "buffered-reader 0.17.0", "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dyn-clone 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2486,6 +2492,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" "checksum dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" "checksum docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f525a586d310c87df72ebcd98009e57f1cc030c8c268305287a476beb653969" +"checksum dyn-clone 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3ec9c7fb9a2ce708751c98e31ccbae74b6ab194f5c8e30cfb7ed62e38b70866" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8944dc8fa28ce4a38f778bd46bf7d923fe73eed5a439398507246c8e017e6f36" "checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" diff --git a/openpgp/Cargo.toml b/openpgp/Cargo.toml index 2b33b04e..f4c1d665 100644 --- a/openpgp/Cargo.toml +++ b/openpgp/Cargo.toml @@ -29,6 +29,7 @@ anyhow = "1" buffered-reader = { path = "../buffered-reader", version = "0.17", default-features = false } base64 = ">= 0.11, < 0.13" bzip2 = { version = "0.3.2", optional = true } +dyn-clone = "1" flate2 = { version = "1.0.1", optional = true } idna = "0.2" lalrpop-util = "0.17" diff --git a/openpgp/src/crypto/backend/nettle.rs b/openpgp/src/crypto/backend/nettle.rs index 73b37569..25fc64eb 100644 --- a/openpgp/src/crypto/backend/nettle.rs +++ b/openpgp/src/crypto/backend/nettle.rs @@ -5,6 +5,7 @@ use nettle::random::{Random, Yarrow}; pub mod aead; pub mod asymmetric; pub mod ecdh; +pub mod hash; /// Fills the given buffer with random data. pub fn random<B: AsMut<[u8]>>(mut buf: B) { diff --git a/openpgp/src/crypto/backend/nettle/hash.rs b/openpgp/src/crypto/backend/nettle/hash.rs new file mode 100644 index 00000000..12271762 --- /dev/null +++ b/openpgp/src/crypto/backend/nettle/hash.rs @@ -0,0 +1,84 @@ +use crate::crypto::hash::Digest; +use crate::{Error, Result}; +use crate::types::{HashAlgorithm}; + +impl<T: nettle::hash::Hash + Clone> Digest for T { + fn digest_size(&self) -> usize { + self.digest_size() + } + + fn update(&mut self, data: &[u8]) { + self.update(data); + } + + fn digest(&mut self, digest: &mut [u8]) { + self.digest(digest); + } +} + +impl HashAlgorithm { + /// Whether Sequoia supports this algorithm. + pub fn is_supported(self) -> bool { + match self { + HashAlgorithm::SHA1 => true, + HashAlgorithm::SHA224 => true, + HashAlgorithm::SHA256 => true, + HashAlgorithm::SHA384 => true, + HashAlgorithm::SHA512 => true, + HashAlgorithm::RipeMD => true, + HashAlgorithm::MD5 => true, + HashAlgorithm::Private(_) => false, + HashAlgorithm::Unknown(_) => false, + HashAlgorithm::__Nonexhaustive => unreachable!(), + } + } + + /// Creates a new hash context for this algorithm. + /// + /// # Errors + /// + /// Fails with `Error::UnsupportedHashAlgorithm` if Sequoia does + /// not support this algorithm. See + /// [`HashAlgorithm::is_supported`]. + /// + /// [`HashAlgorithm::is_supported`]: #method.is_supported + pub fn new_hasher(self) -> Result<Box<dyn Digest>> { + use nettle::hash::{Sha224, Sha256, Sha384, Sha512}; + use nettle::hash::insecure_do_not_use::{ + Sha1, + Md5, + Ripemd160, + }; + + match self { + HashAlgorithm::SHA1 => Ok(Box::new(Sha1::default())), + HashAlgorithm::SHA224 => Ok(Box::new(Sha224::default())), + HashAlgorithm::SHA256 => Ok(Box::new(Sha256::default())), + HashAlgorithm::SHA384 => Ok(Box::new(Sha384::default())), + HashAlgorithm::SHA512 => Ok(Box::new(Sha512::default())), + HashAlgorithm::MD5 => Ok(Box::new(Md5::default())), + HashAlgorithm::RipeMD => Ok(Box::new(Ripemd160::default())), + HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => + Err(Error::UnsupportedHashAlgorithm(self).into()), + HashAlgorithm::__Nonexhaustive => unreachable!(), + } + } + + /// Returns the ASN.1 OID of this hash algorithm. + pub fn oid(self) -> Result<&'static [u8]> { + use nettle::rsa; + + match self { + HashAlgorithm::SHA1 => Ok(rsa::ASN1_OID_SHA1), + HashAlgorithm::SHA224 => Ok(rsa::ASN1_OID_SHA224), + HashAlgorithm::SHA256 => Ok(rsa::ASN1_OID_SHA256), + HashAlgorithm::SHA384 => Ok(rsa::ASN1_OID_SHA384), + HashAlgorithm::SHA512 => Ok(rsa::ASN1_OID_SHA512), + HashAlgorithm::MD5 => Ok(rsa::ASN1_OID_MD5), + HashAlgorithm::RipeMD => Ok(rsa::ASN1_OID_RIPEMD160), + HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => + Err(Error::UnsupportedHashAlgorithm(self).into()), + HashAlgorithm::__Nonexhaustive => unreachable!(), + } + } +} diff --git a/openpgp/src/crypto/hash.rs b/openpgp/src/crypto/hash.rs index 15e1e965..fce57572 100644 --- a/openpgp/src/crypto/hash.rs +++ b/openpgp/src/crypto/hash.rs @@ -2,6 +2,8 @@ use std::convert::TryFrom; +use dyn_clone::DynClone; + use crate::HashAlgorithm; use crate::packet::Key; use crate::packet::UserID; @@ -10,7 +12,6 @@ use crate::packet::key; use crate::packet::key::Key4; use crate::packet::Signature; use crate::packet::signature::{self, Signature4}; -use crate::Error; use crate::Result; use crate::types::Timestamp; @@ -21,6 +22,26 @@ use std::io::{self, Write}; // hashed to files /tmp/hash-N, where N is a number. const DUMP_HASHED_VALUES: Option<&str> = None; +/// Hasher capable of calculating a digest for the input byte stream. +pub trait Digest: DynClone { + /// Size of the digest in bytes + fn digest_size(&self) -> usize; + + /// Writes data into the hash function. + fn update(&mut self, data: &[u8]); + + /// Finalizes the hash function and writes the digest into the + /// provided slice. + /// + /// Resets the hash function contexts. + /// + /// `digest` must be at least `self.digest_size()` bytes large, + /// otherwise the digest will be truncated. + fn digest(&mut self, digest: &mut [u8]); +} + +dyn_clone::clone_trait_object!(Digest); + /// State of a hash function. /// /// This provides an abstract interface to the hash functions used in @@ -49,7 +70,7 @@ const DUMP_HASHED_VALUES: Option<&str> = None; #[derive(Clone)] pub struct Context { algo: HashAlgorithm, - ctx: Box<dyn nettle::hash::Hash>, + ctx: Box<dyn Digest>, } impl Context { @@ -92,22 +113,6 @@ impl io::Write for Context { } impl HashAlgorithm { - /// Whether Sequoia supports this algorithm. - pub fn is_supported(self) -> bool { - match self { - HashAlgorithm::SHA1 => true, - HashAlgorithm::SHA224 => true, - HashAlgorithm::SHA256 => true, - HashAlgorithm::SHA384 => true, - HashAlgorithm::SHA512 => true, - HashAlgorithm::RipeMD => true, - HashAlgorithm::MD5 => true, - HashAlgorithm::Private(_) => false, - HashAlgorithm::Unknown(_) => false, - HashAlgorithm::__Nonexhaustive => unreachable!(), - } - } - /// Creates a new hash context for this algorithm. /// /// # Errors @@ -118,64 +123,27 @@ impl HashAlgorithm { /// /// [`HashAlgorithm::is_supported`]: #method.is_supported pub fn context(self) -> Result<Context> { - use nettle::hash::{Sha224, Sha256, Sha384, Sha512}; - use nettle::hash::insecure_do_not_use::{ - Sha1, - Md5, - Ripemd160, - }; - - let c: Result<Box<dyn nettle::hash::Hash>> = match self { - HashAlgorithm::SHA1 => Ok(Box::new(Sha1::default())), - HashAlgorithm::SHA224 => Ok(Box::new(Sha224::default())), - HashAlgorithm::SHA256 => Ok(Box::new(Sha256::default())), - HashAlgorithm::SHA384 => Ok(Box::new(Sha384::default())), - HashAlgorithm::SHA512 => Ok(Box::new(Sha512::default())), - HashAlgorithm::MD5 => Ok(Box::new(Md5::default())), - HashAlgorithm::RipeMD => Ok(Box::new(Ripemd160::default())), - HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => - Err(Error::UnsupportedHashAlgorithm(self).into()), - HashAlgorithm::__Nonexhaustive => unreachable!(), - }; - - c.map(|ctx| Context { - algo: self, - ctx: if let Some(prefix) = DUMP_HASHED_VALUES { - Box::new(HashDumper::new(ctx, prefix)) - } else { - ctx - }, - }) - } - - /// Returns the ASN.1 OID of this hash algorithm. - pub fn oid(self) -> Result<&'static [u8]> { - use nettle::rsa; - - match self { - HashAlgorithm::SHA1 => Ok(rsa::ASN1_OID_SHA1), - HashAlgorithm::SHA224 => Ok(rsa::ASN1_OID_SHA224), - HashAlgorithm::SHA256 => Ok(rsa::ASN1_OID_SHA256), - HashAlgorithm::SHA384 => Ok(rsa::ASN1_OID_SHA384), - HashAlgorithm::SHA512 => Ok(rsa::ASN1_OID_SHA512), - HashAlgorithm::MD5 => Ok(rsa::ASN1_OID_MD5), - HashAlgorithm::RipeMD => Ok(rsa::ASN1_OID_RIPEMD160), - HashAlgorithm::Private(_) | HashAlgorithm::Unknown(_) => - Err(Error::UnsupportedHashAlgorithm(self).into()), - HashAlgorithm::__Nonexhaustive => unreachable!(), - } + self.new_hasher() + .map(|hasher| Context { + algo: self, + ctx: if let Some(prefix) = DUMP_HASHED_VALUES { + Box::new(HashDumper::new(hasher, prefix)) + } else { + hasher + }, + }) } } struct HashDumper { - h: Box<dyn nettle::hash::Hash>, + hasher: Box<dyn Digest>, sink: File, filename: String, written: usize, } impl HashDumper { - fn new(h: Box<dyn nettle::hash::Hash>, prefix: &str) -> Self { + fn new(hasher: Box<dyn Digest>, prefix: &str) -> Self { let mut n = 0; let mut filename; let sink = loop { @@ -189,7 +157,7 @@ impl HashDumper { }; eprintln!("HashDumper: Writing to {}...", &filename); HashDumper { - h, + hasher, sink, filename, written: 0, @@ -197,6 +165,17 @@ impl HashDumper { } } +impl Clone for HashDumper { + fn clone(&self) -> HashDumper { + // We only ever create instances of HashDumper when debugging. + // Whenever we're cloning an instance, just open another file for + // inspection. + let prefix = DUMP_HASHED_VALUES + .expect("cloning a HashDumper but DUMP_HASHED_VALUES wasn't specified"); + HashDumper::new(self.hasher.clone(), prefix) + } +} + impl Drop for HashDumper { fn drop(&mut self) { eprintln!("HashDumper: Wrote {} bytes to {}...", self.written, @@ -204,20 +183,17 @@ impl Drop for HashDumper { } } -impl nettle::hash::Hash for HashDumper { +impl Digest for HashDumper { fn digest_size(&self) -> usize { - self.h.digest_size() + self.hasher.digest_size() } fn update(&mut self, data: &[u8]) { - self.h.update(data); + self.hasher.update(data); self.sink.write_all(data).unwrap(); self.written += data.len(); } fn digest(&mut self, digest: &mut [u8]) { - self.h.digest(digest); - } - fn box_clone(&self) -> Box<dyn nettle::hash::Hash> { - Box::new(Self::new(self.h.box_clone(), &DUMP_HASHED_VALUES.unwrap())) + self.hasher.digest(digest); } } |