186 lines
6.9 KiB
Rust
186 lines
6.9 KiB
Rust
use crate::bytes::Bytes;
|
|
use crate::dsa;
|
|
use crate::rsa;
|
|
use num_bigint::BigUint;
|
|
use openssl::bn::BigNum;
|
|
use openssl::bn::BigNumContext;
|
|
use openssl::error::ErrorStack;
|
|
use openssl::sha::sha256;
|
|
|
|
pub fn challenge41() -> Option<()> {
|
|
let (public_key, private_key) = rsa::rsa_gen_keys().ok()?;
|
|
|
|
let i = BigNum::from_u32(1337).ok()?;
|
|
let c = rsa::rsa_encrypt_unpadded(&i, &public_key).ok()?;
|
|
let m = rsa::rsa_decrypt_unpadded(&c, &private_key).ok()?;
|
|
assert_eq!(i, m, "rsa is broken");
|
|
|
|
let mut ctx = BigNumContext::new().ok()?;
|
|
// Let S be a random number > 1 mod N. Doesn't matter what.
|
|
let mut s = BigNum::new().ok()?;
|
|
public_key.n.rand_range(&mut s).ok()?;
|
|
// C' = ((S**E mod N) C) mod N
|
|
let mut c2 = BigNum::new().ok()?;
|
|
c2.mod_exp(&s, &public_key.e, &public_key.n, &mut ctx)
|
|
.ok()?;
|
|
let c2 = &(&c2 * &c) % &public_key.n;
|
|
let p2 = rsa::rsa_decrypt_unpadded(&c2, &private_key).ok()?;
|
|
|
|
// P'
|
|
// P = --- mod N
|
|
// S
|
|
let p2 = &(&p2 * &rsa::invmod(&s, &public_key.n).ok()?) % &public_key.n;
|
|
assert_eq!(i, p2, "message recovery oracle failed");
|
|
|
|
println!("[okay] Challenge 41: implement unpadded message recovery oracle");
|
|
Some(())
|
|
}
|
|
|
|
pub fn challenge42() -> Option<()> {
|
|
let (public_key, private_key) = rsa::rsa_gen_keys().ok()?;
|
|
|
|
let i = BigNum::from_u32(1337).ok()?;
|
|
let c = rsa::rsa_encrypt(&i, &public_key).ok()?;
|
|
let m = rsa::rsa_decrypt(&c, &private_key).ok()?;
|
|
assert_eq!(i, m, "rsa is broken");
|
|
|
|
let m = "a message to verify";
|
|
let sig = rsa::rsa_sign(&m, &private_key).ok()?;
|
|
let sig_ok = rsa::rsa_verify(&m, &public_key, &sig).ok()?;
|
|
assert!(sig_ok, "RSA verify does not work");
|
|
assert!(
|
|
rsa::rsa_verify("other message", &public_key, &sig).ok()? == false,
|
|
"RSA verify does not work"
|
|
);
|
|
|
|
let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig).ok()?;
|
|
assert!(sig_ok, "RSA verify does not work");
|
|
assert!(
|
|
rsa::rsa_verify_insecure("other message", &public_key, &sig).ok()? == false,
|
|
"RSA verify does not work"
|
|
);
|
|
|
|
fn cube_root(n: &BigNum) -> Result<BigNum, ErrorStack> {
|
|
let b = BigUint::from_bytes_be(&n.to_vec());
|
|
let b = b.nth_root(3);
|
|
BigNum::from_slice(&b.to_bytes_be())
|
|
}
|
|
|
|
fn _cube(n: &BigNum) -> BigNum {
|
|
n * &(n * n)
|
|
}
|
|
|
|
pub fn rsa_fake_sign(m: &str) -> Result<BigNum, ErrorStack> {
|
|
let hash = sha256(m.as_bytes());
|
|
let mut v = vec![0x0, 0x1, 0xff, 0x0];
|
|
v.append(&mut hash.to_vec());
|
|
while v.len() < 128 {
|
|
v.push(0);
|
|
}
|
|
|
|
// Add one to the cube root to ensure that when the number is
|
|
// cubed again it contains the desired signature.
|
|
let sig_cubed = BigNum::from_slice(&v)?;
|
|
let mut sig = cube_root(&sig_cubed)?;
|
|
sig.add_word(1)?;
|
|
|
|
Ok(sig)
|
|
}
|
|
|
|
let m = "hi mom";
|
|
let sig = rsa_fake_sign(&m).ok()?;
|
|
let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig).ok()?;
|
|
assert!(sig_ok, "RSA fake sign does not work");
|
|
|
|
println!("[okay] Challenge 42: Bleichenbacher's e=3 RSA Attack");
|
|
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<Option<BigNum>, 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!("[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()?;
|
|
let sig = keys.sign(¶ms, &msg).ok()?;
|
|
let result = keys.verify(¶ms, &msg, &sig).ok()?;
|
|
assert!(result, "verify failed unexpectedly");
|
|
|
|
let recovered_x = dsa::recover_x(¶ms, &msg, &sig).ok()?;
|
|
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
|
|
}
|