Prepare for Bleichenbacher's PKCS attack

This commit is contained in:
2023-01-22 20:04:09 -05:00
parent c6c6167112
commit 6abf32c361
3 changed files with 84 additions and 56 deletions

View File

@@ -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"));
}

View File

@@ -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 {

View File

@@ -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(())
}