Solve challenge 44 DSA recoery from repeated nonce

This commit is contained in:
2023-01-13 21:09:13 -05:00
parent ec5a19f54e
commit 47976540d5
2 changed files with 94 additions and 15 deletions

View File

@@ -71,7 +71,8 @@ fn main() {
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"));
}
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"));
}

View File

@@ -143,7 +143,6 @@ pub mod challenge43 {
}
pub fn challenge43() -> Option<()> {
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(&params).ok()?;
@@ -169,17 +168,28 @@ pub fn challenge43() -> Option<()> {
BigNum::from_hex_str("0954edd5e0afe5542a4adf012611a91912a3ec16").ok()?,
"Recovery from none failed"
);
println!("[okay] Challenge 43: DSA key recovery from nonce");
Some(())
}
pub mod challenge44 {
use std::io::{BufReader, BufRead};
use crate::bytes::Bytes;
use openssl::bn::BigNum;
use crate::dsa;
use crate::rsa;
use openssl::bn::BigNum;
use openssl::bn::BigNumContext;
use std::io::{BufRead, BufReader};
pub fn read_dsa_signed_messages() {
pub struct DsaSignedMsg {
pub msg: Bytes,
pub r: BigNum,
pub s: BigNum,
pub m: BigNum,
}
pub fn read_dsa_signed_messages() -> Vec<DsaSignedMsg> {
let file = std::fs::File::open("data/44.txt").unwrap();
let mut result = Vec::new();
let mut lines: Vec<String> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();
// each message cosists of four lines: msg, s, r, m (sha1 hash of msg)
for line in lines.chunks_mut(4) {
@@ -188,22 +198,90 @@ pub mod challenge44 {
line[2].remove_matches("r: ");
line[3].remove_matches("m: ");
let msg = Bytes::from_utf8(&line[0]);
let s = BigNum::from_dec_str(&line[1]).unwrap();
let r = BigNum::from_dec_str(&line[2]).unwrap();
let m = BigNum::from_hex_str(&line[3]).unwrap();
assert_eq!(dsa::h(&msg).unwrap(), m, "Message hash from data/44.txt does not match");
assert_eq!(
dsa::h(&msg).unwrap(),
m,
"Message hash from data/44.txt does not match"
);
let d = DsaSignedMsg { msg, r, s, m };
result.push(d);
}
return result;
}
}
pub fn calculate_k(d1: &DsaSignedMsg, d2: &DsaSignedMsg) -> Option<BigNum> {
// Recovers k if d1 and d2 have been created with the same k.
let mut ctx = BigNumContext::new().ok()?;
let params = dsa::DsaParameters::new().ok()?;
let mut num = BigNum::new().ok()?;
let mut den = BigNum::new().ok()?;
let mut den_inv = BigNum::new().ok()?;
let mut result = BigNum::new().ok()?;
// (m1 - m2)
// k = --------- mod q
// (s1 - s2)
num.mod_sub(&d1.m, &d2.m, &params.q, &mut ctx).ok()?;
den.mod_sub(&d1.s, &d2.s, &params.q, &mut ctx).ok()?;
if den_inv.mod_inverse(&den, &params.q, &mut ctx).is_err() {
return None;
}
result.mod_mul(&num, &den_inv, &params.q, &mut ctx).ok()?;
return Some(result);
}
pub fn find_x() -> Option<BigNum> {
let mut ctx = BigNumContext::new().ok()?;
let params = dsa::DsaParameters::new().ok()?;
let msgs = read_dsa_signed_messages();
let num_msgs = msgs.len();
for i in 0..(num_msgs - 1) {
for j in i..num_msgs {
let k = calculate_k(&msgs[i], &msgs[j]);
if k.is_none() {
continue;
}
let k = k.unwrap();
let m = &msgs[i];
let r_inv = rsa::invmod(&m.r, &params.q);
let k_inv = rsa::invmod(&k, &params.q);
// Reconstruct x from k. If two messages have been encrypted with the same k, we
// can then use the x to get the same signature s and r. In that case we have found
// the private key x and return it.
if r_inv.is_ok() && k_inv.is_ok() {
let r_inv = r_inv.ok()?;
let k_inv = k_inv.ok()?;
let x = &(&r_inv * &(&(&m.s * &k) - &m.m)) % &params.q;
let mut r = BigNum::from_u32(0).ok()?;
r.mod_exp(&params.g, &k, &params.p, &mut ctx).ok()?;
r = &r % &params.q;
let s = &(&k_inv * &(&m.m + &(&x * &r))) % &params.q;
if s == m.s && r == m.r {
return Some(x);
}
}
}
}
return None;
}
}
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(&params).ok()?;
let sig = keys.sign(&params, &msg).ok()?;
let result = keys.verify(&params, &msg, &sig).ok()?;
assert!(result, "verify failed unexpectedly");
challenge44::read_dsa_signed_messages();
let x = challenge44::find_x()?;
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("ca8f6f7c66fa362d40760d135b763eb8527d3d52").ok()?,
"Recovery from repeated nonce failed"
);
println!("[okay] Challenge 44: DSA nonce recovery from repeated nonce");
Some(())
}
pub fn challenge45() -> Option<()> {
println!("[xxxx] Challenge 45: DSA parameter tampering");
None
}