diff --git a/src/dsa.rs b/src/dsa.rs index 1e8c232..5c6193e 100644 --- a/src/dsa.rs +++ b/src/dsa.rs @@ -44,7 +44,14 @@ impl DsaKeys { params.q.rand_range(&mut x)?; // Compute y := g^x mod p y.mod_exp(¶ms.g, &x, ¶ms.p, &mut ctx)?; - Ok(Self { x: x, y: y }) + Ok(Self { x, y }) + } + + pub fn new_from_x(params: &DsaParameters, x: BigNum) -> Result { + let mut ctx = BigNumContext::new()?; + let mut y = BigNum::new()?; + y.mod_exp(¶ms.g, &x, ¶ms.p, &mut ctx)?; + Ok(Self { x, y }) } pub fn sign(&self, params: &DsaParameters, msg: &Bytes) -> Result { @@ -108,7 +115,7 @@ pub fn recover_x(params: &DsaParameters, msg: &Bytes, sig: &DsaSig) -> Result Result { +pub fn h(message: &Bytes) -> Result { let mut s1 = sha1::Sha1::default(); BigNum::from_slice(&(s1.hash(message).0)) } diff --git a/src/main.rs b/src/main.rs index 01f9b96..9af97db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,8 +68,9 @@ fn main() { set5::challenge38().unwrap_or_else(|| println!("[fail] challenge 38")); set5::challenge39().unwrap_or_else(|| println!("[fail] challenge 39")); set5::challenge40().unwrap_or_else(|| println!("[fail] challenge 40")); + set6::challenge41().unwrap_or_else(|| println!("[fail] challenge 41")); + set6::challenge42().unwrap_or_else(|| println!("[fail] challenge 42")); } - set6::challenge41().unwrap_or_else(|| println!("[fail] challenge 41")); - set6::challenge42().unwrap_or_else(|| println!("[fail] challenge 42")); set6::challenge43().unwrap_or_else(|| println!("[fail] challenge 43")); + set6::challenge44().unwrap_or_else(|| println!("[fail] challenge 44")); } diff --git a/src/set6.rs b/src/set6.rs index 8925524..3a3f853 100644 --- a/src/set6.rs +++ b/src/set6.rs @@ -96,8 +96,55 @@ pub fn challenge42() -> Option<()> { Some(()) } +pub mod challenge43 { + use crate::bytes::Bytes; + use crate::dsa; + use crate::rsa; + use openssl::bn::BigNum; + use openssl::error::ErrorStack; + + pub fn recover_x() -> Result, ErrorStack> { + // I used the parameters above. + let params = dsa::DsaParameters::new()?; + // I generated a keypair. My pubkey y is given: + let y = BigNum::from_hex_str("84ad4719d044495496a3201c8ff484feb45b962e7302e56a392aee4abab3e4bdebf2955b4736012f21a08084056b19bcd7fee56048e004e44984e2f411788efdc837a0d2e5abb7b555039fd243ac01f0fb2ed1dec568280ce678e931868d23eb095fde9d3779191b8c0299d6e07bbb283e6633451e535c45513b2d33c99ea17")?; + + // I signed the following message. + let msg = Bytes::from_utf8("For those that envy a MC it can be hazardous to your health\nSo be friendly, a matter of life and death, just like a etch-a-sketch\n"); + // My SHA1 for this string was d2d0714f014a9784047eaeccf956520045c45265. + assert_eq!( + dsa::h(&msg)?, + BigNum::from_hex_str("d2d0714f014a9784047eaeccf956520045c45265")? + ); + + // They provide s and r as decimal integers and not hex strings. I + // converted the decimals to hex. I took me a couple of hours to figure + // that out. + let mut sig = dsa::DsaSig { + r: BigNum::from_hex_str("60019cacdc56eedf8e080984bfa898c8c5c419a8")?, + s: BigNum::from_hex_str("961f2062efc3c68db965a90c924cf76580ec1bbc")?, + k: BigNum::from_u32(0)?, + }; + let msg_h = dsa::h(&msg)?; + let r_inv = rsa::invmod(&sig.r, ¶ms.q)?; + + // I signed this string with a broken implemention of DSA that generated "k" + // values between 0 and 2^16. What's my private key? + for k in 0..2_u32.pow(16) { + // I get the signature. + sig.k = BigNum::from_u32(k)?; + let x = &(&r_inv * &(&(&sig.s * &sig.k) - &msg_h)) % ¶ms.q; + let keys = dsa::DsaKeys::new_from_x(¶ms, x)?; + if y == keys.y { + return Ok(Some(keys.x)); + } + } + Ok(None) + } +} + pub fn challenge43() -> Option<()> { - println!("[xxxx] Challenge 43: TBD"); + println!("[okay] Challenge 43: DSA key recovery from nonce"); let msg = Bytes::from_utf8("hello, world!"); let params = dsa::DsaParameters::new().ok()?; let keys = dsa::DsaKeys::new(¶ms).ok()?; @@ -106,7 +153,33 @@ pub fn challenge43() -> Option<()> { assert!(result, "verify failed unexpectedly"); let recovered_x = dsa::recover_x(¶ms, &msg, &sig).ok()?; - assert_eq!(recovered_x, keys.x, "DSA recovery failed"); + assert_eq!(recovered_x, keys.x, "DSA x recovery failed"); + let recovered_keys = dsa::DsaKeys::new_from_x(¶ms, recovered_x).ok()?; + assert_eq!(recovered_keys.y, keys.y, "DSA y recovery failed"); + let msg = Bytes::from_utf8("hello world!"); + let result = keys.verify(¶ms, &msg, &sig).ok()?; + assert!(!result, "verify succeeded unexpectedly"); + + // Its SHA-1 fingerprint (after being converted to hex) is: + // 0954edd5e0afe5542a4adf012611a91912a3ec16 + let x = challenge43::recover_x().ok()??; + let x_as_hex_str = x.to_hex_str().ok()?.to_ascii_lowercase(); + assert_eq!( + dsa::h(&Bytes::from_utf8(x_as_hex_str.as_ref())).ok()?, + BigNum::from_hex_str("0954edd5e0afe5542a4adf012611a91912a3ec16").ok()?, + "Recovery from none failed" + ); Some(()) } + +pub fn challenge44() -> Option<()> { + println!("[xxxx] Challenge 44: DSA nonce recovery from repeated nonce"); + let msg = Bytes::from_utf8("hello, world!"); + let params = dsa::DsaParameters::new().ok()?; + let keys = dsa::DsaKeys::new(¶ms).ok()?; + let sig = keys.sign(¶ms, &msg).ok()?; + let result = keys.verify(¶ms, &msg, &sig).ok()?; + assert!(result, "verify failed unexpectedly"); + None +}