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::Bytes;
|
||||||
|
use crate::bytes_base64::BytesBase64;
|
||||||
use crate::dsa;
|
use crate::dsa;
|
||||||
use crate::rsa;
|
use crate::rsa;
|
||||||
use num_bigint::BigUint;
|
use num_bigint::BigUint;
|
||||||
@@ -340,6 +341,51 @@ pub fn challenge45() -> Option<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn challenge46() -> Option<()> {
|
pub fn challenge46() -> Option<()> {
|
||||||
println!("[xxxx] Challenge 46: RSA parity oracle");
|
let m_b64 = BytesBase64::from_base64("VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ").ok()?;
|
||||||
None
|
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