sha1collisiondetection 0.3.4

SHA-1 hash function with collision detection and mitigation
Documentation
//! An implementation of the [SHA-1][1] cryptographic hash algorithm.
//!
//! This is a port of Marc Stevens' sha1collisiondetection algorithm to
//! Rust.  The code is translated from C to Rust using c2rust.
//!
//! To improve the translation, the code is transformed first, replacing
//! macros with inline functions.  Running the test suite using `make
//! check` tests the correctness of the transformation.
//!
//! This crate, like the C implementation, is distributed under the MIT
//! Software License.
//!
//! # Installation of the command line utility
//!
//! The command line utility is a drop-in replacement for coreutils'
//! `sha1sum` utility.  It can be installed, for example, using cargo:
//!
//! ```sh
//! $ cargo install sha1collisiondetection
//! [...]
//! $ sha1cdsum --help
//! sha1cdsum 0.2.3
//! Print or check SHA1 (160-bit) checksums with collision detection.
//!
//! USAGE:
//!     sha1cdsum [FLAGS] [files]...
//! [...]
//! $ sha1cdsum test/*1.*
//! 4f3d9be4a472c4dae83c6314aa6c36a064c1fd14 *coll* test/sha-mbles-1.bin
//! 16e96b70000dd1e7c85b8368ee197754400e58ec *coll* test/shattered-1.pdf
//! ```
//!
//! //! # About
//!
//! This library was designed as near drop-in replacements for common
//! SHA-1 libraries.  They will compute the SHA-1 hash of any given
//! file and additionally will detect cryptanalytic collision attacks
//! against SHA-1 present in each file. It is very fast and takes less
//! than twice the amount of time as regular SHA-1.
//!
//! More specifically they will detect any cryptanalytic collision attack
//! against SHA-1 using any of the top 32 SHA-1 disturbance vectors with
//! probability 1:
//!
//! ```text
//!     I(43,0), I(44,0), I(45,0), I(46,0), I(47,0), I(48,0), I(49,0), I(50,0), I(51,0), I(52,0),
//!     I(46,2), I(47,2), I(48,2), I(49,2), I(50,2), I(51,2),
//!     II(45,0), II(46,0), II(47,0), II(48,0), II(49,0), II(50,0), II(51,0), II(52,0), II(53,0), II(54,0), II(55,0), II(56,0),
//!     II(46,2), II(49,2), II(50,2), II(51,2)
//! ```
//!
//! The possibility of false positives can be neglected as the probability
//! is smaller than 2^-90.
//!
//! The library supports both an indicator flag that applications can
//! check and act on, as well as a special _safe-hash_ mode that returns
//! the real SHA-1 hash when no collision was detected and a different
//! _safe_ hash when a collision was detected.  Colliding files will have
//! the same SHA-1 hash, but will have different unpredictable
//! safe-hashes.  This essentially enables protection of applications
//! against SHA-1 collisions with no further changes in the application,
//! e.g., digital signature forgeries based on SHA-1 collisions
//! automatically become invalid.
//!
//! For the theoretical explanation of collision detection see the
//! award-winning paper on _Counter-Cryptanalysis_:
//!
//! Counter-cryptanalysis, Marc Stevens, CRYPTO 2013, Lecture Notes in
//! Computer Science, vol. 8042, Springer, 2013, pp. 129-146,
//! https://marc-stevens.nl/research/papers/C13-S.pdf
//!
//! # Developers
//!
//! The C implementation of the collision detection algorithm is
//! implemented by:
//!
//! - Marc Stevens, CWI Amsterdam (https://marc-stevens.nl)
//! - Dan Shumow, Microsoft Research (https://www.microsoft.com/en-us/research/people/danshu/)
//!
//! The C implementation is maintained
//! [here](https://github.com/cr-marcstevens/sha1collisiondetection).
//!
//! Please report issues with the rust port
//! [here](https://gitlab.com/sequoia-pgp/sha1collisiondetection).
//!
//! # Usage
//!
//! ```rust
//! use hex_literal::hex;
//! use sha1collisiondetection::Sha1CD;
//!
//! // create a Sha1CD object
//! let mut hasher = Sha1CD::default();
//!
//! // process input message
//! hasher.update(b"hello world");
//!
//! // acquire hash digest in the form of GenericArray,
//! // which in this case is equivalent to [u8; 20]
//! let result = hasher.finalize_cd().unwrap();
//! assert_eq!(result[..], hex!("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"));
//! ```
//!
//! If this crate's "digest-trait" feature is used, `Sha1CD` also
//! implements the `Digest` trait from the `digest` crate.  Also see
//! [RustCrypto/hashes][2] readme.
//!
//! [1]: https://en.wikipedia.org/wiki/SHA-1
//! [2]: https://github.com/RustCrypto/hashes

#![no_std]
#![warn(missing_docs, rust_2018_idioms)]

use core::fmt;

#[cfg(feature = "std")]
extern crate std;

mod sha1;
use crate::sha1 as sys;
mod ubc_check;

/// Public re-export for consumers of this crate.
pub use generic_array;
use generic_array::{GenericArray, typenum::consts::U20};
/// The digest output.
pub type Output = GenericArray<u8, U20>;

/// Configures the collision-detecting SHA-1 algorithm.
#[derive(Default)]
pub struct Builder(sys::SHA1_CTX);

impl Builder {
    /// Configures collision mitigation.
    ///
    /// Collision attacks are thwarted by hashing a detected
    /// near-collision block 3 times.  Think of it as extending SHA-1
    /// from 80-steps to 240-steps for such blocks: The best collision
    /// attacks against SHA-1 have complexity about 2^60, thus for
    /// 240-steps an immediate lower-bound for the best cryptoanalytic
    /// attacks would be 2^180.  An attacker would be better off using
    /// a generic birthday search of complexity 2^80.
    ///
    /// Enabling safe SHA-1 hashing will result in the correct SHA-1
    /// hash for messages where no collision attack was detected, but
    /// it will result in a different SHA-1 hash for messages where a
    /// collision attack was detected.  This will automatically
    /// invalidate SHA-1 based digital signature forgeries.
    ///
    /// Enabled by default.
    pub fn safe_hash(mut self, v: bool) -> Self {
        self.0.set_safe_hash(v);
        self
    }

    /// Configures use of Unavoidable Bitconditions.
    ///
    /// This provides a significant speed up.
    ///
    /// Enabled by default.
    pub fn use_ubc(mut self, v: bool) -> Self {
        self.0.set_use_UBC(v);
        self
    }

    /// Configures collision detection.
    ///
    /// Enabled by default.
    pub fn detect_collisions(mut self, v: bool) -> Self {
        self.0.set_use_detect_coll(v);
        self
    }

    /// Finalizes the configuration.
    pub fn build(self) -> Sha1CD {
        Sha1CD(self.0)
    }
}

/// Structure representing the state of a SHA-1 computation.
#[derive(Clone)]
pub struct Sha1CD(sys::SHA1_CTX);

impl fmt::Debug for Sha1CD {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("Sha1CD { ... }")
    }
}

impl Default for Sha1CD {
    fn default() -> Self {
        Builder::default().build()
    }
}

#[cfg(feature = "digest-trait")]
pub use digest::{self, Digest};

#[cfg(feature = "digest-trait")]
use digest::{FixedOutput, HashMarker, OutputSizeUser, Reset, Update};

#[cfg(feature = "digest-trait")]
/// The digest output for use with the digest traits.
type DigestOutput = digest::generic_array::GenericArray<
        u8, digest::generic_array::typenum::consts::U20>;

#[cfg(feature = "digest-trait")]
impl HashMarker for Sha1CD {}

#[cfg(feature = "digest-trait")]
impl Update for Sha1CD {
    fn update(&mut self, input: &[u8]) {
        Sha1CD::update(self, input);
    }
}

#[cfg(feature = "digest-trait")]
impl Reset for Sha1CD {
    fn reset(&mut self) {
        Sha1CD::reset(self);
    }
}

#[cfg(feature = "digest-trait")]
impl OutputSizeUser for Sha1CD {
    type OutputSize = digest::generic_array::typenum::consts::U20;
}

#[cfg(feature = "digest-trait")]
impl FixedOutput for Sha1CD {
    fn finalize_into(mut self, out: &mut DigestOutput) {
        let mut digest = Output::default();
        let _ = self.finalize_into_dirty_cd(&mut digest);
        out.copy_from_slice(&digest);
    }
}

#[cfg(feature = "oid")]
use const_oid::{AssociatedOid, ObjectIdentifier};

#[cfg(feature = "oid")]
impl AssociatedOid for Sha1CD {
    const OID: ObjectIdentifier =
        ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
}

impl Sha1CD {
    /// Configures the algorithm.
    pub fn configure() -> Builder {
        Builder::default()
    }

    /// Digest input data.
    ///
    /// This method can be called repeatedly, e.g. for processing
    /// streaming messages.
    pub fn update(&mut self, input: impl AsRef<[u8]>) {
        self.0.update(input.as_ref());
    }

    /// Reset hasher instance to its initial state.
    pub fn reset(&mut self) {
        let safe_hash = self.0.safe_hash;
        let ubc_check = self.0.ubc_check;
        let detect_coll = self.0.detect_coll;
        let reduced_round_coll = self.0.reduced_round_coll;
        let callback = self.0.callback;
        self.0.reset();
        self.0.safe_hash = safe_hash;
        self.0.ubc_check = ubc_check;
        self.0.detect_coll = detect_coll;
        self.0.reduced_round_coll = reduced_round_coll;
        self.0.callback = callback;
    }

    /// Retrieve result and consume hasher instance, reporting
    /// collisions.
    pub fn finalize_cd(mut self)
                       -> Result<Output, Collision> {
        let mut digest = Output::default();
        self.finalize_into_dirty_cd(&mut digest)?;
        Ok(digest)
    }

    /// Retrieve result and reset hasher instance, reporting
    /// collisions.
    ///
    /// This method sometimes can be more efficient compared to hasher
    /// re-creation.
    pub fn finalize_reset_cd(&mut self)
                             -> Result<Output, Collision> {
        let mut digest = Output::default();
        self.finalize_into_dirty_cd(&mut digest)?;
        Sha1CD::reset(self);
        Ok(digest)
    }

    /// Computes the digest and returns if a collision attack was
    /// detected.
    ///
    /// In case of collisions, the digest will still be returned.
    /// Depending on whether or not the collision mitigation is
    /// enabled (see [`Builder::safe_hash`]), either an attacker
    /// controlled digest as produced by the SHA-1 function, or a
    /// digest computed by a modified SHA-1 function mitigating the
    /// attack.
    ///
    /// By default, the mitigations are enabled, hence this is a safer
    /// variant of Sha1, which invalidates all signatures over any
    /// objects hashing to an attacker-controlled digest.
    ///
    /// [`Builder::safe_hash`]: struct.Builder.html#method.safe_hash
    pub fn finalize_into_dirty_cd(&mut self, out: &mut Output)
                                  -> Result<(), Collision> {
        if ! self.0.finalize(out.as_mut()) {
            Ok(())
        } else {
            Err(Collision::new())
        }
    }
}

#[cfg(feature = "std")]
impl std::io::Write for Sha1CD {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        Sha1CD::update(self, buf);
        Ok(buf.len())
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

/// A detected collision.
///
/// This is returned by [`Sha1CD::finalize_cd`] and similar functions
/// when a collision attack has been detected.
///
/// [`Sha1CD::finalize_cd`]: struct.Sha1CD.html#method.finalize_cd
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Collision {
    // XXX: Register a callback and store information about the
    // colliding blocks.
}

impl fmt::Display for Collision {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("SHA-1 Collision detected")
    }
}

impl Collision {
    fn new() -> Self {
        Collision {
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for Collision {}