Implement parity oracle for challenge 46

This commit is contained in:
2023-01-15 22:09:46 -05:00
parent 70cecb0d9e
commit 365bde182d

View File

@@ -1,4 +1,5 @@
use crate::bytes::Bytes;
use crate::bytes_base64::BytesBase64;
use crate::dsa;
use crate::rsa;
use num_bigint::BigUint;
@@ -340,6 +341,51 @@ pub fn challenge45() -> Option<()> {
}
pub fn challenge46() -> Option<()> {
println!("[xxxx] Challenge 46: RSA parity oracle");
None
let m_b64 = BytesBase64::from_base64("VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ").ok()?;
let m = m_b64.to_bytes();
let (public_key, private_key) = rsa::rsa_gen_keys().ok()?;
let cipher = rsa::rsa_encrypt_str(&m.to_utf8(), &public_key).ok()?;
assert!(
rsa::rsa_decrypt_str(&cipher, &private_key).ok()? == m.to_utf8(),
"rsa does not work"
);
let parity_oracle = |cipher: &BigNum| -> bool {
// Return true or false based on whether the decrypted plaintext was even or odd.
let cleartext: BigNum = rsa::rsa_decrypt(cipher, &private_key).unwrap();
!cleartext.is_bit_set(0)
};
// a == 97 => odd
let c = rsa::rsa_encrypt_str("string_a", &public_key).ok()?;
assert!(!parity_oracle(&c), "parity oracle doesn't work");
// a == 98 => even
let c = rsa::rsa_encrypt_str("string_b", &public_key).ok()?;
assert!(parity_oracle(&c), "parity oracle doesn't work");
// RSA ciphertexts are just numbers. You can do trivial math on them. You can for instance
// multiply a ciphertext by the RSA-encryption of another number; the corresponding plaintext
// will be the product of those two numbers. If you double a ciphertext (multiply it by
// (2**e)%n), the resulting plaintext will (obviously) be either even or odd. If the plaintext
// after doubling is even, doubling the plaintext didn't wrap the modulus --- the modulus is a
// prime number. That means the plaintext is less than half the modulus.
// - You can repeatedly apply this heuristic, once per bit of the message, checking your oracle
// function each time.
// - Your decryption function starts with bounds for the plaintext of [0,n].
// - Each iteration of the decryption cuts the bounds in half; either the upper bound is reduced
// by half, or the lower bound is.
// - After log2(n) iterations, you have the decryption of the message.
// Print the upper bound of the message as a string at each iteration; you'll see the message
// decrypt "hollywood style".
let attack = |cipher: &BigNum| -> Bytes { Bytes(vec![]) };
let p = attack(&cipher);
assert!(m == p, "RSA parity attack did not work");
println!("[okay] Challenge 46: RSA parity oracle");
Some(())
}