From 6abf32c3619e2e802b354a4d45a978348ae47c15 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Sun, 22 Jan 2023 20:04:09 -0500 Subject: [PATCH] Prepare for Bleichenbacher's PKCS attack --- src/main.rs | 10 ++--- src/rsa.rs | 16 +++++--- src/set6.rs | 114 +++++++++++++++++++++++++++++++--------------------- 3 files changed, 84 insertions(+), 56 deletions(-) diff --git a/src/main.rs b/src/main.rs index b5d5202..5995a9b 100644 --- a/src/main.rs +++ b/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")); } diff --git a/src/rsa.rs b/src/rsa.rs index bf3df29..a0c782b 100644 --- a/src/rsa.rs +++ b/src/rsa.rs @@ -51,13 +51,13 @@ fn generate_random_prime(bits: i32) -> Result { 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 { 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 Result 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 { 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(()) }