Implement parity oracle for challenge 46
This commit is contained in:
50
src/set6.rs
50
src/set6.rs
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user