diff --git a/sha1/src/compress/collision.rs b/sha1/src/compress/collision.rs index e4a572ab8..7bb33052c 100644 --- a/sha1/src/compress/collision.rs +++ b/sha1/src/compress/collision.rs @@ -684,7 +684,7 @@ pub fn compress(state: &mut [u32; 5], ctx: &mut DetectionState, blocks: &[[u8; B let mut state_cpy = *state; for block in blocks.iter() { - if ctx.detect_collision { + if ctx.config.detect_collision { ctx.ihv1.copy_from_slice(&state_cpy); } @@ -693,7 +693,7 @@ pub fn compress(state: &mut [u32; 5], ctx: &mut DetectionState, blocks: &[[u8; B } let DetectionState { - detect_collision, + config, m1, state_58, state_65, @@ -702,8 +702,8 @@ pub fn compress(state: &mut [u32; 5], ctx: &mut DetectionState, blocks: &[[u8; B compression_states(&mut state_cpy, &block_u32, m1, state_58, state_65); - if *detect_collision { - let ubc_mask = if ctx.ubc_check { + if config.detect_collision { + let ubc_mask = if ctx.config.ubc_check { crate::ubc_check::ubc_check(&ctx.m1) } else { 0xFFFFFFFF @@ -744,7 +744,7 @@ pub fn compress(state: &mut [u32; 5], ctx: &mut DetectionState, blocks: &[[u8; B | (ihvtmp[2] ^ state_cpy[2]) | (ihvtmp[3] ^ state_cpy[3]) | (ihvtmp[4] ^ state_cpy[4]))) - || (ctx.reduced_round_collision + || (ctx.config.reduced_round_collision && 0 == ((ctx.ihv1[0] ^ ctx.ihv2[0]) | (ctx.ihv1[1] ^ ctx.ihv2[1]) | (ctx.ihv1[2] ^ ctx.ihv2[2]) @@ -753,7 +753,7 @@ pub fn compress(state: &mut [u32; 5], ctx: &mut DetectionState, blocks: &[[u8; B { ctx.found_collision = true; - if ctx.safe_hash { + if ctx.config.safe_hash { compression_w(&mut state_cpy, &mut ctx.m1); compression_w(&mut state_cpy, &mut ctx.m1); } diff --git a/sha1/src/lib.rs b/sha1/src/lib.rs index 2e06136e6..ad831b5a6 100644 --- a/sha1/src/lib.rs +++ b/sha1/src/lib.rs @@ -45,20 +45,54 @@ pub struct Sha1Core { detection: DetectionState, } +/// Configuration for collision detection. +#[cfg(feature = "collision")] +#[derive(Clone)] +pub struct CollisionDetectionConfig { + /// Should we detect collisions at all? Default: true + pub detect_collision: bool, + /// Should a fix be automatically be applied, or the original hash be returned? Default: true + pub safe_hash: bool, + /// Should unavoidable bitconditions be used to speed up the check? Default: true + pub ubc_check: bool, + /// Should reduced round collisions be used? Default: false + pub reduced_round_collision: bool, +} + +#[cfg(feature = "collision")] +impl Default for CollisionDetectionConfig { + fn default() -> Self { + Self { + detect_collision: true, + safe_hash: true, + ubc_check: true, + reduced_round_collision: false, + } + } +} + +#[cfg(feature = "collision")] +impl CollisionDetectionConfig { + /// Create a Sha1 with a specific collision detection configuration. + pub fn build(self) -> Sha1 { + let core = Sha1Core { + detection: DetectionState { + config: self, + ..Default::default() + }, + ..Default::default() + }; + CoreWrapper::from_core(core) + } +} + /// The internal state used to do collision detection. #[cfg(feature = "collision")] #[derive(Clone)] pub struct DetectionState { - /// Should we detect collisions at all? - detect_collision: bool, - /// Should a fix be automatically be applied, or the original hash be returned? - safe_hash: bool, - /// Should unavoidable bitconditions be used to speed up the check? - ubc_check: bool, + config: CollisionDetectionConfig, /// Has a collision been detected? found_collision: bool, - /// Has a reduced round collision been detected? - reduced_round_collision: bool, ihv1: [u32; 5], ihv2: [u32; 5], m1: [u32; 80], @@ -72,10 +106,7 @@ pub struct DetectionState { impl Default for DetectionState { fn default() -> Self { Self { - detect_collision: true, - safe_hash: false, - ubc_check: true, - reduced_round_collision: false, + config: Default::default(), found_collision: false, ihv1: Default::default(), ihv2: Default::default(), diff --git a/sha1/tests/data/sha-mbles-1.bin b/sha1/tests/data/sha-mbles-1.bin new file mode 100644 index 000000000..5a7c30e97 Binary files /dev/null and b/sha1/tests/data/sha-mbles-1.bin differ diff --git a/sha1/tests/data/sha-mbles-2.bin b/sha1/tests/data/sha-mbles-2.bin new file mode 100644 index 000000000..fe3917840 Binary files /dev/null and b/sha1/tests/data/sha-mbles-2.bin differ diff --git a/sha1/tests/data/sha1_reducedsha_coll.bin b/sha1/tests/data/sha1_reducedsha_coll.bin new file mode 100644 index 000000000..462333622 --- /dev/null +++ b/sha1/tests/data/sha1_reducedsha_coll.bin @@ -0,0 +1 @@ +~9:pऄޥVZ-kqpDx$ . (8<+sC>NM]{$,+0ThEC !R6+f^j@,9|<r \ No newline at end of file diff --git a/sha1/tests/data/shattered-1.pdf b/sha1/tests/data/shattered-1.pdf new file mode 100644 index 000000000..ba9aaa145 Binary files /dev/null and b/sha1/tests/data/shattered-1.pdf differ diff --git a/sha1/tests/data/shattered-2.pdf b/sha1/tests/data/shattered-2.pdf new file mode 100644 index 000000000..b621eeccd Binary files /dev/null and b/sha1/tests/data/shattered-2.pdf differ diff --git a/sha1/tests/mod.rs b/sha1/tests/mod.rs index 43321252e..9220520e5 100644 --- a/sha1/tests/mod.rs +++ b/sha1/tests/mod.rs @@ -2,6 +2,9 @@ use digest::dev::{feed_rand_16mib, fixed_reset_test}; use hex_literal::hex; use sha1::{Digest, Sha1}; +#[cfg(feature = "collision")] +use sha1::CollisionDetectionConfig; + digest::new_test!(sha1_main, "sha1", Sha1, fixed_reset_test); #[test] @@ -16,13 +19,90 @@ fn sha1_rand() { #[cfg(feature = "collision")] #[test] -fn sha1_shambles() { - let mut h = Sha1::new(); +fn shambles_1() { + let input = &include_bytes!("./data/sha-mbles-1.bin")[..]; - // TODO: actually load shambles data ;) - h.update("shambles"); - assert_eq!( - h.finalize(), - hex!("0409b8f154f8d5f273085c2c7bc369dc5a188c3b"), - ); + // No detection. + let mut ctx = CollisionDetectionConfig { + detect_collision: false, + ..Default::default() + } + .build(); + ctx.update(input); + let d = ctx.finalize(); + assert_eq!(&d[..], hex!("8ac60ba76f1999a1ab70223f225aefdc78d4ddc0")); + + // No mitigation. + let mut ctx = CollisionDetectionConfig { + safe_hash: false, + ..Default::default() + } + .build(); + ctx.update(input); + + // TODO: check for error + let d = ctx.finalize(); + assert_eq!(&d[..], hex!("8ac60ba76f1999a1ab70223f225aefdc78d4ddc0")); + + // No mitigation, no optimization. + let mut ctx = CollisionDetectionConfig { + safe_hash: false, + ubc_check: false, + ..Default::default() + } + .build(); + ctx.update(input); + let d = ctx.finalize(); + // TODO: check for error + assert_eq!(&d[..], hex!("8ac60ba76f1999a1ab70223f225aefdc78d4ddc0")); + + // With mitigation. + let mut ctx = Sha1::new(); + ctx.update(input); + let d = ctx.finalize(); + assert_eq!(&d[..], hex!("4f3d9be4a472c4dae83c6314aa6c36a064c1fd14")); +} + +#[test] +fn shambles_2() { + let input = &include_bytes!("./data/sha-mbles-2.bin")[..]; + + // No detection. + let mut ctx = CollisionDetectionConfig { + detect_collision: false, + ..Default::default() + } + .build(); + ctx.update(input); + let d = ctx.finalize(); + assert_eq!(&d[..], hex!("8ac60ba76f1999a1ab70223f225aefdc78d4ddc0")); + + // No mitigation. + let mut ctx = CollisionDetectionConfig { + safe_hash: false, + ..Default::default() + } + .build(); + ctx.update(input); + let d = ctx.finalize(); + // TODO: check for error + assert_eq!(&d[..], hex!("8ac60ba76f1999a1ab70223f225aefdc78d4ddc0")); + + // No mitigation, no optimization. + let mut ctx = CollisionDetectionConfig { + safe_hash: false, + ubc_check: false, + ..Default::default() + } + .build(); + ctx.update(input); + let d = ctx.finalize(); + // TODO: check for error + assert_eq!(&d[..], hex!("8ac60ba76f1999a1ab70223f225aefdc78d4ddc0")); + + // With mitigation. + let mut ctx = Sha1::new(); + ctx.update(input); + let d = ctx.finalize(); + assert_eq!(&d[..], hex!("9ed5d77a4f48be1dbf3e9e15650733eb850897f2")); }