Prepare for Bleichenbacher's PKCS attack
This commit is contained in:
10
src/main.rs
10
src/main.rs
@@ -27,7 +27,7 @@ mod srp;
|
||||
mod utils;
|
||||
|
||||
fn main() {
|
||||
const RUN_ALL: bool = false;
|
||||
const RUN_ALL: bool = true;
|
||||
if RUN_ALL {
|
||||
set1::challenge1();
|
||||
set1::challenge2();
|
||||
@@ -69,12 +69,12 @@ fn main() {
|
||||
set5::challenge38().unwrap_or_else(|| println!("[fail] challenge 38"));
|
||||
set5::challenge39().unwrap_or_else(|| println!("[fail] challenge 39"));
|
||||
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::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::challenge44().unwrap_or_else(|| println!("[fail] challenge 44"));
|
||||
set6::challenge45().unwrap_or_else(|| println!("[fail] challenge 45"));
|
||||
set6::challenge46().unwrap_or_else(|| println!("[fail] challenge 46"));
|
||||
set6::challenge47().unwrap_or_else(|| println!("[fail] challenge 47"));
|
||||
set6::challenge46().unwrap_or_else(|_| println!("[fail] challenge 46"));
|
||||
set6::challenge47().unwrap_or_else(|_| println!("[fail] challenge 47"));
|
||||
}
|
||||
|
||||
16
src/rsa.rs
16
src/rsa.rs
@@ -51,13 +51,13 @@ fn generate_random_prime(bits: i32) -> Result<BigNum, ErrorStack> {
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
pub fn rsa_gen_keys() -> Result<(RsaPublicKey, RsaPrivateKey), ErrorStack> {
|
||||
pub fn rsa_gen_keys_with_size(p_bits: i32, q_bits: i32) -> Result<(RsaPublicKey, RsaPrivateKey), ErrorStack> {
|
||||
let mut ctx = BigNumContext::new()?;
|
||||
|
||||
loop {
|
||||
// Generate 2 random primes.
|
||||
let mut p = generate_random_prime(512)?;
|
||||
let mut q = generate_random_prime(512)?;
|
||||
let mut p = generate_random_prime(p_bits)?;
|
||||
let mut q = generate_random_prime(q_bits)?;
|
||||
|
||||
// Let n be p * q. Your RSA math is modulo n.
|
||||
let mut n = BigNum::new()?;
|
||||
@@ -83,6 +83,10 @@ pub fn rsa_gen_keys() -> Result<(RsaPublicKey, RsaPrivateKey), ErrorStack> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rsa_gen_keys() -> Result<(RsaPublicKey, RsaPrivateKey), ErrorStack> {
|
||||
rsa_gen_keys_with_size(512, 512)
|
||||
}
|
||||
|
||||
pub fn invmod(a: &BigNum, n: &BigNum) -> Result<BigNum, ErrorStack> {
|
||||
fn extended_gcd(a: BigNum, b: BigNum) -> Result<(BigNum, BigNum, BigNum), ErrorStack> {
|
||||
// credit: https://www.dcode.fr/extended-gcd
|
||||
@@ -140,7 +144,7 @@ pub fn rsa_padding_add_pkcs1(m: &BigNum, to_len: i32) -> Result<BigNum, ErrorSta
|
||||
let padding_str_len: usize = (to_len - 3 - from_len).try_into().unwrap();
|
||||
let mut v = vec![0x0; 3 + padding_str_len];
|
||||
v[0] = 0x0;
|
||||
v[1] = 0x1;
|
||||
v[1] = 0x2;
|
||||
for i in 2..padding_str_len + 2 {
|
||||
v[i] = 0xff;
|
||||
}
|
||||
@@ -156,7 +160,7 @@ pub fn rsa_padding_remove_pkcs1(m: &BigNum, pad_to: i32) -> Result<BigNum, Error
|
||||
// first byte is zero and therefore num_bytes is 1 smaller than expected
|
||||
assert!(m.num_bytes() + 1 == pad_to, "Padding length incorrect");
|
||||
assert!(v[0] == 0, "PKCS1 padding incorrect");
|
||||
assert!(v[1] == 1, "PKCS1 padding incorrect");
|
||||
assert!(v[1] == 2, "PKCS1 padding incorrect");
|
||||
while v[i] == 0xff {
|
||||
i += 1;
|
||||
}
|
||||
@@ -235,7 +239,7 @@ pub fn rsa_verify_insecure(
|
||||
// them by looking for 00h 01h ... ffh 00h ASN.1 HASH.
|
||||
let v = m.to_vec_padded(pad_to)?;
|
||||
assert!(v[0] == 0, "PKCS1 padding incorrect");
|
||||
assert!(v[1] == 1, "PKCS1 padding incorrect");
|
||||
assert!(v[1] == 2, "PKCS1 padding incorrect");
|
||||
let mut i = 2;
|
||||
while i < v.len() - 1 {
|
||||
if v[i] == 0xff && v[i + 1] == 0x0 {
|
||||
|
||||
114
src/set6.rs
114
src/set6.rs
@@ -9,56 +9,55 @@ 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()?;
|
||||
pub fn challenge41() -> Result<(), ErrorStack> {
|
||||
let (public_key, private_key) = rsa::rsa_gen_keys()?;
|
||||
|
||||
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()?;
|
||||
let i = BigNum::from_u32(1337)?;
|
||||
let c = rsa::rsa_encrypt_unpadded(&i, &public_key)?;
|
||||
let m = rsa::rsa_decrypt_unpadded(&c, &private_key)?;
|
||||
assert_eq!(i, m, "rsa is broken");
|
||||
|
||||
let mut ctx = BigNumContext::new().ok()?;
|
||||
let mut ctx = BigNumContext::new()?;
|
||||
// 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()?;
|
||||
let mut s = BigNum::new()?;
|
||||
public_key.n.rand_range(&mut s)?;
|
||||
// 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 mut c2 = BigNum::new()?;
|
||||
c2.mod_exp(&s, &public_key.e, &public_key.n, &mut ctx)?;
|
||||
let c2 = &(&c2 * &c) % &public_key.n;
|
||||
let p2 = rsa::rsa_decrypt_unpadded(&c2, &private_key).ok()?;
|
||||
let p2 = rsa::rsa_decrypt_unpadded(&c2, &private_key)?;
|
||||
|
||||
// P'
|
||||
// P = --- mod N
|
||||
// S
|
||||
let p2 = &(&p2 * &rsa::invmod(&s, &public_key.n).ok()?) % &public_key.n;
|
||||
let p2 = &(&p2 * &rsa::invmod(&s, &public_key.n)?) % &public_key.n;
|
||||
assert_eq!(i, p2, "message recovery oracle failed");
|
||||
|
||||
println!("[okay] Challenge 41: implement unpadded message recovery oracle");
|
||||
Some(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn challenge42() -> Option<()> {
|
||||
let (public_key, private_key) = rsa::rsa_gen_keys().ok()?;
|
||||
pub fn challenge42() -> Result<(), ErrorStack> {
|
||||
let (public_key, private_key) = rsa::rsa_gen_keys()?;
|
||||
|
||||
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()?;
|
||||
let i = BigNum::from_u32(1337)?;
|
||||
let c = rsa::rsa_encrypt(&i, &public_key)?;
|
||||
let m = rsa::rsa_decrypt(&c, &private_key)?;
|
||||
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()?;
|
||||
let sig = rsa::rsa_sign(&m, &private_key)?;
|
||||
let sig_ok = rsa::rsa_verify(&m, &public_key, &sig)?;
|
||||
assert!(sig_ok, "RSA verify does not work");
|
||||
assert!(
|
||||
rsa::rsa_verify("other message", &public_key, &sig).ok()? == false,
|
||||
rsa::rsa_verify("other message", &public_key, &sig)? == false,
|
||||
"RSA verify does not work"
|
||||
);
|
||||
|
||||
let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig).ok()?;
|
||||
let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig)?;
|
||||
assert!(sig_ok, "RSA verify does not work");
|
||||
assert!(
|
||||
rsa::rsa_verify_insecure("other message", &public_key, &sig).ok()? == false,
|
||||
rsa::rsa_verify_insecure("other message", &public_key, &sig)? == false,
|
||||
"RSA verify does not work"
|
||||
);
|
||||
|
||||
@@ -74,7 +73,7 @@ pub fn challenge42() -> Option<()> {
|
||||
|
||||
pub fn rsa_fake_sign(m: &str) -> Result<BigNum, ErrorStack> {
|
||||
let hash = sha256(m.as_bytes());
|
||||
let mut v = vec![0x0, 0x1, 0xff, 0x0];
|
||||
let mut v = vec![0x0, 0x2, 0xff, 0x0];
|
||||
v.append(&mut hash.to_vec());
|
||||
while v.len() < 128 {
|
||||
v.push(0);
|
||||
@@ -90,12 +89,12 @@ pub fn challenge42() -> Option<()> {
|
||||
}
|
||||
|
||||
let m = "hi mom";
|
||||
let sig = rsa_fake_sign(&m).ok()?;
|
||||
let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig).ok()?;
|
||||
let sig = rsa_fake_sign(&m)?;
|
||||
let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig)?;
|
||||
assert!(sig_ok, "RSA fake sign does not work");
|
||||
|
||||
println!("[okay] Challenge 42: Bleichenbacher's e=3 RSA Attack");
|
||||
Some(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub mod challenge43 {
|
||||
@@ -341,15 +340,15 @@ pub fn challenge45() -> Option<()> {
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn challenge46() -> Option<()> {
|
||||
let m_b64 = BytesBase64::from_base64("VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ").ok()?;
|
||||
let m = BigNum::from_slice(&m_b64.to_bytes().0).ok()?;
|
||||
let (public_key, private_key) = rsa::rsa_gen_keys().ok()?;
|
||||
pub fn challenge46() -> Result<(), ErrorStack> {
|
||||
let m_b64 = BytesBase64::from_base64("VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ").unwrap();
|
||||
let m = BigNum::from_slice(&m_b64.to_bytes().0)?;
|
||||
let (public_key, private_key) = rsa::rsa_gen_keys()?;
|
||||
|
||||
let c = rsa::rsa_encrypt_unpadded(&m, &public_key).ok()?;
|
||||
let n = BigNum::from_slice(&public_key.n.to_vec()).ok()?;
|
||||
let c = rsa::rsa_encrypt_unpadded(&m, &public_key)?;
|
||||
let n = bnclone(&public_key.n);
|
||||
assert!(
|
||||
rsa::rsa_decrypt_unpadded(&c, &private_key).ok()? == m,
|
||||
rsa::rsa_decrypt_unpadded(&c, &private_key)? == m,
|
||||
"rsa does not work"
|
||||
);
|
||||
|
||||
@@ -359,18 +358,18 @@ pub fn challenge46() -> Option<()> {
|
||||
!cleartext.is_bit_set(0)
|
||||
};
|
||||
|
||||
let c_a = rsa::rsa_encrypt_unpadded(&BigNum::from_u32(97).ok()?, &public_key).ok()?;
|
||||
let c_a = rsa::rsa_encrypt_unpadded(&BigNum::from_u32(97)?, &public_key)?;
|
||||
assert!(!parity_oracle(&c_a), "parity oracle odd doesn't work");
|
||||
|
||||
let c_b = rsa::rsa_encrypt_unpadded(&BigNum::from_u32(98).ok()?, &public_key).ok()?;
|
||||
let c_b = rsa::rsa_encrypt_unpadded(&BigNum::from_u32(98)?, &public_key)?;
|
||||
assert!(parity_oracle(&c_b), "parity oracle even doesn't work");
|
||||
|
||||
|
||||
// Double by multiplying with (2**e) % n
|
||||
let mut ctx = BigNumContext::new().ok()?;
|
||||
let two = BigNum::from_u32(2).ok()?;
|
||||
let mut multiplier = BigNum::new().ok()?;
|
||||
multiplier.mod_exp(&two, &public_key.e, &n, &mut ctx).ok()?;
|
||||
let mut ctx = BigNumContext::new()?;
|
||||
let two = BigNum::from_u32(2)?;
|
||||
let mut multiplier = BigNum::new()?;
|
||||
multiplier.mod_exp(&two, &public_key.e, &n, &mut ctx)?;
|
||||
let double = |c: BigNum| -> BigNum {
|
||||
return &(&c * &multiplier) % &private_key.n;
|
||||
};
|
||||
@@ -403,14 +402,39 @@ pub fn challenge46() -> Option<()> {
|
||||
Ok(&(&upper * &n) / &div)
|
||||
};
|
||||
|
||||
let s = solve(bnclone(&c), bnclone(&n)).ok()?;
|
||||
let s = solve(bnclone(&c), bnclone(&n))?;
|
||||
assert!(m == s, "RSA parity attack did not work");
|
||||
|
||||
println!("[okay] Challenge 46: RSA parity oracle");
|
||||
Some(())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn challenge47() -> Option<()> {
|
||||
pub fn challenge47() -> Result<(), ErrorStack> {
|
||||
|
||||
// Generate a 256 bit keypair (that is, p and q will each be 128 bit primes), [n, e, d].
|
||||
let (public_key, private_key) = rsa::rsa_gen_keys_with_size(128, 128)?;
|
||||
|
||||
// PKCS1.5-pad a short message, like "kick it, CC", and call it "m". Encrypt to to get "c".
|
||||
let m = Bytes::from_utf8("kick it, CC");
|
||||
let m = BigNum::from_slice(&m.0)?;
|
||||
let n = bnclone(&public_key.n);
|
||||
let n_bytes = n.num_bytes();
|
||||
|
||||
// Build an oracle function, just like you did in the last exercise, but have it check for
|
||||
// plaintext[0] == 0 and plaintext[1] == 2.
|
||||
let oracle = |cipher: &BigNum| -> bool {
|
||||
let cleartext: BigNum = rsa::rsa_decrypt_unpadded(cipher, &private_key).unwrap();
|
||||
let v = cleartext.to_vec_padded(n_bytes).unwrap();
|
||||
return v[0] == 0x0 && v[1] == 0x2;
|
||||
};
|
||||
|
||||
// Decrypt "c" using your padding oracle.
|
||||
let c_unpadded = rsa::rsa_encrypt_unpadded(&m, &public_key)?;
|
||||
let c = rsa::rsa_encrypt(&m, &public_key)?;
|
||||
|
||||
assert!(!oracle(&c_unpadded), "oracle wrongly thinks unpadded message is padded");
|
||||
assert!(oracle(&c), "oracle wrongly thinks padded message is not padded");
|
||||
|
||||
println!("[xxxx] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)");
|
||||
None
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user