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;
|
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"));
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/rsa.rs
16
src/rsa.rs
@@ -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 {
|
||||||
|
|||||||
114
src/set6.rs
114
src/set6.rs
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user