From 70cecb0d9e4d2f865c7e21cc3a7fde90054a2428 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Sat, 14 Jan 2023 10:37:37 -0500 Subject: [PATCH] Solve challenge 45 DSA parameter tampering --- src/dsa.rs | 15 +++++++------- src/main.rs | 1 + src/set6.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/dsa.rs b/src/dsa.rs index 5c6193e..0e82275 100644 --- a/src/dsa.rs +++ b/src/dsa.rs @@ -59,13 +59,14 @@ impl DsaKeys { let mut k = BigNum::new()?; let mut r = BigNum::from_u32(0)?; let zero = BigNum::from_u32(0)?; - while r == zero { - // Choose an integer k randomly from { 1 … q − 1 } - params.q.rand_range(&mut k)?; - // Compute r := (g^k mod p) mod q. In the unlikely case that r=0, start again. - r.mod_exp(¶ms.g, &k, ¶ms.p, &mut ctx)?; - r = &r % ¶ms.q; - } + + // Choose an integer k randomly from { 1 … q − 1 } + params.q.rand_range(&mut k)?; + // Compute r := (g^k mod p) mod q. + r.mod_exp(¶ms.g, &k, ¶ms.p, &mut ctx)?; + r = &r % ¶ms.q; + // In the unlikely case that r=0, crash. + assert!(r != zero, "r is zero"); // Compute s := (k^(−1) * ( H ( m ) + x r ) ) mod q. let s = &(&rsa::invmod(&k, ¶ms.q)? * &(&h(msg)? + &(&self.x * &r))) % ¶ms.q; diff --git a/src/main.rs b/src/main.rs index af6fcb1..30488d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -75,4 +75,5 @@ fn main() { set6::challenge43().unwrap_or_else(|| println!("[fail] challenge 43")); set6::challenge44().unwrap_or_else(|| println!("[fail] challenge 44")); set6::challenge45().unwrap_or_else(|| println!("[fail] challenge 45")); + set6::challenge46().unwrap_or_else(|| println!("[fail] challenge 46")); } diff --git a/src/set6.rs b/src/set6.rs index 9902958..90fe093 100644 --- a/src/set6.rs +++ b/src/set6.rs @@ -282,6 +282,64 @@ pub fn challenge44() -> Option<()> { } pub fn challenge45() -> Option<()> { - println!("[xxxx] Challenge 45: DSA parameter tampering"); + // Part 1: 0 mod p + // + // Use the parameters from the previous exercise, but substitute 0 for "g". Generate a + // signature. You will notice something bad. + // + // Observation: + // + // - If g=0, because of r := (g^k mod p) mod q, it follows r=0. + // - When verifying, v := ((g^u1 * y^u2) % p) % q). + // - Therefore, v=0, and v == r is always true for any signature. + // + // Our implementation catches this case, both for signing and verifying. + + // Part 2: 1 mod p + let mut params = dsa::DsaParameters::new().ok()?; + fn generate_magic_signature( + params: &mut dsa::DsaParameters, + ) -> Result { + let mut ctx = BigNumContext::new()?; + + //Now, try (p+1) as "g". With this "g", you can generate a magic signature s, r for any DSA + //public key that will validate against any string. + params.g = ¶ms.p + &BigNum::from_u32(1)?; + + // For arbitrary z: + // r = ((y**z) % p) % q + let mut r = BigNum::from_u32(0)?; + let z = BigNum::from_u32(1337)?; + r.mod_exp(¶ms.g, &z, ¶ms.p, &mut ctx)?; + r = &r % ¶ms.q; + + // r + // s = --- % q + // z + let s = &rsa::invmod(&z, ¶ms.q)? * &r; + + // k doesn't matter + let k = BigNum::from_u32(0)?; + + Ok(dsa::DsaSig { r, s, k }) + } + + let sig = generate_magic_signature(&mut params).ok()?; + let keys = dsa::DsaKeys::new(¶ms).ok()?; + + let msg = Bytes::from_utf8("Hello, world"); + let result = keys.verify(¶ms, &msg, &sig).ok()?; + assert!(result, "verify failed unexpectedly"); + + let msg = Bytes::from_utf8("Goodbye, world"); + let result = keys.verify(¶ms, &msg, &sig).ok()?; + assert!(result, "verify failed unexpectedly"); + + println!("[okay] Challenge 45: DSA parameter tampering"); + Some(()) +} + +pub fn challenge46() -> Option<()> { + println!("[xxxx] Challenge 46: RSA parity oracle"); None }