summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Matuszewski <igor@sequoia-pgp.org>2020-04-10 01:39:20 +0200
committerIgor Matuszewski <igor@sequoia-pgp.org>2020-06-22 11:57:07 +0200
commit61c66c3df5b12f79102eccddf479f58c1a3bdd63 (patch)
tree226e4dfa536e2c78a9e66a616831deb34e91b054
parentc8c207e931f204e5746eaff7dd57382048d26492 (diff)
openpgp: Move Nettle hashing impls to the backend module
-rw-r--r--Cargo.lock7
-rw-r--r--openpgp/Cargo.toml1
-rw-r--r--openpgp/src/crypto/backend/nettle.rs1
-rw-r--r--openpgp/src/crypto/backend/nettle/hash.rs84
-rw-r--r--openpgp/src/crypto/hash.rs124
5 files changed, 143 insertions, 74 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5045794b..ea1a1840 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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);
}
}