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; mod utils;
fn main() { fn main() {
const RUN_ALL: bool = false; const RUN_ALL: bool = true;
if RUN_ALL { if RUN_ALL {
set1::challenge1(); set1::challenge1();
set1::challenge2(); set1::challenge2();
@@ -69,12 +69,12 @@ fn main() {
set5::challenge38().unwrap_or_else(|| println!("[fail] challenge 38")); set5::challenge38().unwrap_or_else(|| println!("[fail] challenge 38"));
set5::challenge39().unwrap_or_else(|| println!("[fail] challenge 39")); set5::challenge39().unwrap_or_else(|| println!("[fail] challenge 39"));
set5::challenge40().unwrap_or_else(|| println!("[fail] challenge 40")); set5::challenge40().unwrap_or_else(|| println!("[fail] challenge 40"));
set6::challenge41().unwrap_or_else(|| println!("[fail] challenge 41")); set6::challenge41().unwrap_or_else(|_| println!("[fail] challenge 41"));
set6::challenge42().unwrap_or_else(|| println!("[fail] challenge 42")); set6::challenge42().unwrap_or_else(|_| println!("[fail] challenge 42"));
} }
set6::challenge43().unwrap_or_else(|| println!("[fail] challenge 43")); set6::challenge43().unwrap_or_else(|| println!("[fail] challenge 43"));
set6::challenge44().unwrap_or_else(|| println!("[fail] challenge 44")); set6::challenge44().unwrap_or_else(|| println!("[fail] challenge 44"));
set6::challenge45().unwrap_or_else(|| println!("[fail] challenge 45")); set6::challenge45().unwrap_or_else(|| println!("[fail] challenge 45"));
set6::challenge46().unwrap_or_else(|| println!("[fail] challenge 46")); set6::challenge46().unwrap_or_else(|_| println!("[fail] challenge 46"));
set6::challenge47().unwrap_or_else(|| println!("[fail] challenge 47")); 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) 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()?; let mut ctx = BigNumContext::new()?;
loop { loop {
// Generate 2 random primes. // Generate 2 random primes.
let mut p = generate_random_prime(512)?; let mut p = generate_random_prime(p_bits)?;
let mut q = generate_random_prime(512)?; let mut q = generate_random_prime(q_bits)?;
// Let n be p * q. Your RSA math is modulo n. // Let n be p * q. Your RSA math is modulo n.
let mut n = BigNum::new()?; 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> { pub fn invmod(a: &BigNum, n: &BigNum) -> Result<BigNum, ErrorStack> {
fn extended_gcd(a: BigNum, b: BigNum) -> Result<(BigNum, BigNum, BigNum), ErrorStack> { fn extended_gcd(a: BigNum, b: BigNum) -> Result<(BigNum, BigNum, BigNum), ErrorStack> {
// credit: https://www.dcode.fr/extended-gcd // 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 padding_str_len: usize = (to_len - 3 - from_len).try_into().unwrap();
let mut v = vec![0x0; 3 + padding_str_len]; let mut v = vec![0x0; 3 + padding_str_len];
v[0] = 0x0; v[0] = 0x0;
v[1] = 0x1; v[1] = 0x2;
for i in 2..padding_str_len + 2 { for i in 2..padding_str_len + 2 {
v[i] = 0xff; 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 // first byte is zero and therefore num_bytes is 1 smaller than expected
assert!(m.num_bytes() + 1 == pad_to, "Padding length incorrect"); assert!(m.num_bytes() + 1 == pad_to, "Padding length incorrect");
assert!(v[0] == 0, "PKCS1 padding 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 { while v[i] == 0xff {
i += 1; i += 1;
} }
@@ -235,7 +239,7 @@ pub fn rsa_verify_insecure(
// them by looking for 00h 01h ... ffh 00h ASN.1 HASH. // them by looking for 00h 01h ... ffh 00h ASN.1 HASH.
let v = m.to_vec_padded(pad_to)?; let v = m.to_vec_padded(pad_to)?;
assert!(v[0] == 0, "PKCS1 padding incorrect"); 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; let mut i = 2;
while i < v.len() - 1 { while i < v.len() - 1 {
if v[i] == 0xff && v[i + 1] == 0x0 { if v[i] == 0xff && v[i + 1] == 0x0 {

View File

@@ -9,56 +9,55 @@ use openssl::bn::BigNumContext;
use openssl::error::ErrorStack; use openssl::error::ErrorStack;
use openssl::sha::sha256; use openssl::sha::sha256;
pub fn challenge41() -> Option<()> { pub fn challenge41() -> Result<(), ErrorStack> {
let (public_key, private_key) = rsa::rsa_gen_keys().ok()?; let (public_key, private_key) = rsa::rsa_gen_keys()?;
let i = BigNum::from_u32(1337).ok()?; let i = BigNum::from_u32(1337)?;
let c = rsa::rsa_encrypt_unpadded(&i, &public_key).ok()?; let c = rsa::rsa_encrypt_unpadded(&i, &public_key)?;
let m = rsa::rsa_decrypt_unpadded(&c, &private_key).ok()?; let m = rsa::rsa_decrypt_unpadded(&c, &private_key)?;
assert_eq!(i, m, "rsa is broken"); 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 S be a random number > 1 mod N. Doesn't matter what.
let mut s = BigNum::new().ok()?; let mut s = BigNum::new()?;
public_key.n.rand_range(&mut s).ok()?; public_key.n.rand_range(&mut s)?;
// C' = ((S**E mod N) C) mod N // C' = ((S**E mod N) C) mod N
let mut c2 = BigNum::new().ok()?; let mut c2 = BigNum::new()?;
c2.mod_exp(&s, &public_key.e, &public_key.n, &mut ctx) c2.mod_exp(&s, &public_key.e, &public_key.n, &mut ctx)?;
.ok()?;
let c2 = &(&c2 * &c) % &public_key.n; 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'
// P = --- mod N // P = --- mod N
// S // 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"); assert_eq!(i, p2, "message recovery oracle failed");
println!("[okay] Challenge 41: implement unpadded message recovery oracle"); println!("[okay] Challenge 41: implement unpadded message recovery oracle");
Some(()) Ok(())
} }
pub fn challenge42() -> Option<()> { pub fn challenge42() -> Result<(), ErrorStack> {
let (public_key, private_key) = rsa::rsa_gen_keys().ok()?; let (public_key, private_key) = rsa::rsa_gen_keys()?;
let i = BigNum::from_u32(1337).ok()?; let i = BigNum::from_u32(1337)?;
let c = rsa::rsa_encrypt(&i, &public_key).ok()?; let c = rsa::rsa_encrypt(&i, &public_key)?;
let m = rsa::rsa_decrypt(&c, &private_key).ok()?; let m = rsa::rsa_decrypt(&c, &private_key)?;
assert_eq!(i, m, "rsa is broken"); assert_eq!(i, m, "rsa is broken");
let m = "a message to verify"; let m = "a message to verify";
let sig = rsa::rsa_sign(&m, &private_key).ok()?; let sig = rsa::rsa_sign(&m, &private_key)?;
let sig_ok = rsa::rsa_verify(&m, &public_key, &sig).ok()?; let sig_ok = rsa::rsa_verify(&m, &public_key, &sig)?;
assert!(sig_ok, "RSA verify does not work"); assert!(sig_ok, "RSA verify does not work");
assert!( 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" "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!(sig_ok, "RSA verify does not work");
assert!( 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" "RSA verify does not work"
); );
@@ -74,7 +73,7 @@ pub fn challenge42() -> Option<()> {
pub fn rsa_fake_sign(m: &str) -> Result<BigNum, ErrorStack> { pub fn rsa_fake_sign(m: &str) -> Result<BigNum, ErrorStack> {
let hash = sha256(m.as_bytes()); 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()); v.append(&mut hash.to_vec());
while v.len() < 128 { while v.len() < 128 {
v.push(0); v.push(0);
@@ -90,12 +89,12 @@ pub fn challenge42() -> Option<()> {
} }
let m = "hi mom"; let m = "hi mom";
let sig = rsa_fake_sign(&m).ok()?; let sig = rsa_fake_sign(&m)?;
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 fake sign does not work"); assert!(sig_ok, "RSA fake sign does not work");
println!("[okay] Challenge 42: Bleichenbacher's e=3 RSA Attack"); println!("[okay] Challenge 42: Bleichenbacher's e=3 RSA Attack");
Some(()) Ok(())
} }
pub mod challenge43 { pub mod challenge43 {
@@ -341,15 +340,15 @@ pub fn challenge45() -> Option<()> {
Some(()) Some(())
} }
pub fn challenge46() -> Option<()> { pub fn challenge46() -> Result<(), ErrorStack> {
let m_b64 = BytesBase64::from_base64("VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ").ok()?; let m_b64 = BytesBase64::from_base64("VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ").unwrap();
let m = BigNum::from_slice(&m_b64.to_bytes().0).ok()?; let m = BigNum::from_slice(&m_b64.to_bytes().0)?;
let (public_key, private_key) = rsa::rsa_gen_keys().ok()?; let (public_key, private_key) = rsa::rsa_gen_keys()?;
let c = rsa::rsa_encrypt_unpadded(&m, &public_key).ok()?; let c = rsa::rsa_encrypt_unpadded(&m, &public_key)?;
let n = BigNum::from_slice(&public_key.n.to_vec()).ok()?; let n = bnclone(&public_key.n);
assert!( assert!(
rsa::rsa_decrypt_unpadded(&c, &private_key).ok()? == m, rsa::rsa_decrypt_unpadded(&c, &private_key)? == m,
"rsa does not work" "rsa does not work"
); );
@@ -359,18 +358,18 @@ pub fn challenge46() -> Option<()> {
!cleartext.is_bit_set(0) !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"); 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"); assert!(parity_oracle(&c_b), "parity oracle even doesn't work");
// Double by multiplying with (2**e) % n // Double by multiplying with (2**e) % n
let mut ctx = BigNumContext::new().ok()?; let mut ctx = BigNumContext::new()?;
let two = BigNum::from_u32(2).ok()?; let two = BigNum::from_u32(2)?;
let mut multiplier = BigNum::new().ok()?; let mut multiplier = BigNum::new()?;
multiplier.mod_exp(&two, &public_key.e, &n, &mut ctx).ok()?; multiplier.mod_exp(&two, &public_key.e, &n, &mut ctx)?;
let double = |c: BigNum| -> BigNum { let double = |c: BigNum| -> BigNum {
return &(&c * &multiplier) % &private_key.n; return &(&c * &multiplier) % &private_key.n;
}; };
@@ -403,14 +402,39 @@ pub fn challenge46() -> Option<()> {
Ok(&(&upper * &n) / &div) 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"); assert!(m == s, "RSA parity attack did not work");
println!("[okay] Challenge 46: RSA parity oracle"); 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)"); println!("[xxxx] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)");
None Ok(())
} }