diff --git a/src/dsa.rs b/src/dsa.rs new file mode 100644 index 0000000..3ea730c --- /dev/null +++ b/src/dsa.rs @@ -0,0 +1,105 @@ +use crate::bytes::Bytes; +use crate::rsa; +use crate::sha1; +use openssl::bn::BigNum; +use openssl::bn::BigNumContext; +use openssl::error::ErrorStack; + +#[derive(Debug)] +pub struct DsaParameters { + pub p: BigNum, + pub q: BigNum, + pub g: BigNum, +} + +#[derive(Debug)] +pub struct DsaKeys { + pub x: BigNum, + pub y: BigNum, +} + +#[derive(Debug)] +pub struct DsaSig { + pub r: BigNum, + pub s: BigNum, +} + +impl DsaParameters { + pub fn new() -> Result { + Ok(Self { + p: BigNum::from_hex_str("800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1")?, + q: BigNum::from_hex_str("f4f47f05794b256174bba6e9b396a7707e563c5b")?, + g: BigNum::from_hex_str("5958c9d3898b224b12672c0b98e06c60df923cb8bc999d119458fef538b8fa4046c8db53039db620c094c9fa077ef389b5322a559946a71903f990f1f7e0e025e2d7f7cf494aff1a0470f5b64c36b625a097f1651fe775323556fe00b3608c887892878480e99041be601a62166ca6894bdd41a7054ec89f756ba9fc95302291")?, + }) + } +} + +impl DsaKeys { + pub fn new(params: &DsaParameters) -> Result { + let mut ctx = BigNumContext::new()?; + let mut x = BigNum::new()?; + let mut y = BigNum::new()?; + // Choose an integer x randomly from { 1 … q − 1 } + 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 }) + } + + pub fn sign(&self, params: &DsaParameters, msg: &Bytes) -> Result { + let mut ctx = BigNumContext::new()?; + 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; + } + + // Compute s := (k^(−1) * ( H ( m ) + x r ) ) mod q. + let s = &(&rsa::invmod(&k, ¶ms.q)? * &(&h(msg)? + &(&self.x * &r))) % ¶ms.q; + + // In the unlikely case that s=0, crash. + assert!(s != zero, "s is zero"); + + Ok(DsaSig { r, s }) + } + + pub fn verify( + &self, + params: &DsaParameters, + msg: &Bytes, + sig: &DsaSig, + ) -> Result { + let mut ctx = BigNumContext::new()?; + let zero = BigNum::from_u32(0)?; + + // Verify that 0 < r < q and 0 < s < q + if !(zero < sig.r && sig.r < params.q && zero < sig.s && sig.s < params.q) { + return Ok(false); + } + + // Compute w := s^(−1) mod q + let w = rsa::invmod(&sig.s, ¶ms.q)?; + let u1 = &(&h(&msg)? * &w) % ¶ms.q; + let u2 = &(&sig.r * &w) % ¶ms.q; + + // v := ((g^u1 * y^u2) % p) % q) + let mut v1 = BigNum::new()?; + v1.mod_exp(¶ms.g, &u1, ¶ms.p, &mut ctx)?; + let mut v2 = BigNum::new()?; + v2.mod_exp(&self.y, &u2, ¶ms.p, &mut ctx)?; + let v = &(&(&v1 * &v2) % ¶ms.p) % ¶ms.q; + + // The signature is valid if and only if v = r + Ok(v == sig.r) + } +} + +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 cf5fc4f..01f9b96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ mod bytes; mod bytes_base64; mod cbc; mod ctr; +mod dsa; mod ecb; mod md4; mod mt19937; @@ -62,12 +63,12 @@ fn main() { set5::challenge33(); set5::challenge34(); set5::challenge35(); + set5::challenge36().unwrap_or_else(|| println!("[fail] challenge 36")); + set5::challenge37().unwrap_or_else(|| println!("[fail] challenge 37")); + 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")); } - set5::challenge36().unwrap_or_else(|| println!("[fail] challenge 36")); - set5::challenge37().unwrap_or_else(|| println!("[fail] challenge 37")); - 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::challenge43().unwrap_or_else(|| println!("[fail] challenge 43")); diff --git a/src/set5.rs b/src/set5.rs index ec1b84e..41dc340 100644 --- a/src/set5.rs +++ b/src/set5.rs @@ -583,6 +583,6 @@ pub fn challenge40() -> Option<()> { // let c = rsa::rsa_padding_remove_pkcs1(&c, n_0.num_bytes()).ok()?; assert_eq!(c, m, "cube root implementation did not work"); - println!("[okay] Challenge 40: implement an E=3 RSA Broadcast attack"); + println!("[okay] Challenge 40: implement an e=3 RSA Broadcast attack"); Some(()) } diff --git a/src/set6.rs b/src/set6.rs index b253616..be7952d 100644 --- a/src/set6.rs +++ b/src/set6.rs @@ -1,3 +1,5 @@ +use crate::bytes::Bytes; +use crate::dsa; use crate::rsa; use num_bigint::BigUint; use openssl::bn::BigNum; @@ -96,5 +98,12 @@ pub fn challenge42() -> Option<()> { pub fn challenge43() -> Option<()> { println!("[xxxx] Challenge 43: TBD"); + let msg = Bytes::from_utf8("hello, world!"); + let params = dsa::DsaParameters::new().ok()?; + let keys = dsa::DsaKeys::new(¶ms).ok()?; + let s = keys.sign(¶ms, &msg).ok()?; + let r = keys.verify(¶ms, &msg, &s).ok()?; + assert!(r, "verify failed unexpectedly"); + Some(()) }