summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2020-11-10 15:28:07 +0100
committerJustus Winter <justus@sequoia-pgp.org>2020-11-12 10:32:00 +0100
commit8b390e8a38d8a31b5ada19a5ebbce924ee777fd8 (patch)
tree730ba04c27a8ceb603d00bffa22c6851a43c54d6
parent91e82329cb50dda79243d933f13fd22888af5cdd (diff)
openpgp: Mitigate collision attacks on SHA-1.
- Use a collision detecting implementation of SHA-1. When a collision attack is detected, the algorithm employs a mitigation, changing the hash function to discriminate the colliding preimage.
-rw-r--r--Cargo.lock13
-rw-r--r--openpgp/Cargo.toml3
-rw-r--r--openpgp/src/crypto/backend.rs2
-rw-r--r--openpgp/src/crypto/backend/sha1cd.rs30
-rw-r--r--openpgp/src/crypto/hash.rs22
5 files changed, 61 insertions, 9 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d774617e..1160d891 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1893,9 +1893,11 @@ dependencies = [
"buffered-reader",
"bzip2",
"chrono",
+ "digest 0.9.0",
"dyn-clone",
"ed25519-dalek",
"flate2",
+ "generic-array 0.14.4",
"idna",
"lalrpop",
"lalrpop-util",
@@ -1908,6 +1910,7 @@ dependencies = [
"rand",
"regex",
"rpassword",
+ "sha1collisiondetection",
"thiserror",
"unicode-normalization",
"win-crypto-ng",
@@ -2024,6 +2027,16 @@ dependencies = [
]
[[package]]
+name = "sha1collisiondetection"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6df2f0600b061bab243ef94c8d077332f2991c232bf44a5e91d0c401523f514"
+dependencies = [
+ "digest 0.9.0",
+ "libc",
+]
+
+[[package]]
name = "sha2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/openpgp/Cargo.toml b/openpgp/Cargo.toml
index 67240aaa..bcb8decf 100644
--- a/openpgp/Cargo.toml
+++ b/openpgp/Cargo.toml
@@ -29,8 +29,10 @@ anyhow = "1"
buffered-reader = { path = "../buffered-reader", version = "0.20", default-features = false }
base64 = "0.13"
bzip2 = { version = "0.4", optional = true }
+digest = "0.9"
dyn-clone = "1"
flate2 = { version = "1.0.1", optional = true }
+generic-array = "0.14"
idna = "0.2"
lalrpop-util = "0.19"
lazy_static = "1.3"
@@ -38,6 +40,7 @@ libc = "0.2"
memsec = { version = "0.6", default-features = false }
nettle = { version = "7", optional = true }
regex = "1"
+sha1collisiondetection = "0.1"
thiserror = "1"
backtrace = "0.3.46"
unicode-normalization = "0.1.9"
diff --git a/openpgp/src/crypto/backend.rs b/openpgp/src/crypto/backend.rs
index abd7d53c..11727d74 100644
--- a/openpgp/src/crypto/backend.rs
+++ b/openpgp/src/crypto/backend.rs
@@ -1,6 +1,8 @@
//! Concrete implementation of the crypto primitives used by the rest of the
//! crypto API.
+pub(crate) mod sha1cd;
+
#[cfg(feature = "crypto-nettle")]
mod nettle;
#[cfg(feature = "crypto-nettle")]
diff --git a/openpgp/src/crypto/backend/sha1cd.rs b/openpgp/src/crypto/backend/sha1cd.rs
new file mode 100644
index 00000000..1de5f078
--- /dev/null
+++ b/openpgp/src/crypto/backend/sha1cd.rs
@@ -0,0 +1,30 @@
+use crate::crypto::hash::Digest;
+use crate::Result;
+
+pub(crate) fn build() -> sha1collisiondetection::Sha1CD {
+ sha1collisiondetection::Builder::default()
+ .detect_collisions(true)
+ .use_ubc(true)
+ .safe_hash(true)
+ .build()
+}
+
+impl Digest for sha1collisiondetection::Sha1CD {
+ fn digest_size(&self) -> usize {
+ 20
+ }
+
+ fn update(&mut self, data: &[u8]) {
+ digest::Update::update(self, data);
+ }
+
+ fn digest(&mut self, digest: &mut [u8]) -> Result<()> {
+ let mut d =
+ generic_array::GenericArray::<u8, digest::consts::U20>::default();
+ let r = self.finalize_into_dirty_cd(&mut d);
+ digest::Reset::reset(self);
+ let l = digest.len().min(d.len());
+ &mut digest[..l].copy_from_slice(&d[..l]);
+ r.map_err(Into::into)
+ }
+}
diff --git a/openpgp/src/crypto/hash.rs b/openpgp/src/crypto/hash.rs
index 5643a30a..5517d205 100644
--- a/openpgp/src/crypto/hash.rs
+++ b/openpgp/src/crypto/hash.rs
@@ -137,15 +137,19 @@ impl HashAlgorithm {
///
/// [`HashAlgorithm::is_supported`]: #method.is_supported
pub fn context(self) -> Result<Context> {
- self.new_hasher()
- .map(|hasher| Context {
- algo: self,
- ctx: if let Some(prefix) = DUMP_HASHED_VALUES {
- Box::new(HashDumper::new(hasher, prefix))
- } else {
- hasher
- },
- })
+ let hasher = match self {
+ HashAlgorithm::SHA1 =>
+ Box::new(crate::crypto::backend::sha1cd::build()),
+ _ => self.new_hasher()?,
+ };
+ Ok(Context {
+ algo: self,
+ ctx: if let Some(prefix) = DUMP_HASHED_VALUES {
+ Box::new(HashDumper::new(hasher, prefix))
+ } else {
+ hasher
+ },
+ })
}
}