Finish implementation of Bleichenbacher's e=3 RSA Attack finally

main
Felix Martin 2022-12-10 21:04:30 -05:00
parent 35f5137518
commit 6133d17f92
3 changed files with 50 additions and 41 deletions

View File

@ -70,4 +70,5 @@ fn main() {
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"));
} }

View File

@ -210,46 +210,45 @@ pub fn rsa_decrypt_str(c: &BigNum, p: &RsaPrivateKey) -> Result<String, ErrorSta
Ok(String::from(std::str::from_utf8(&m.to_vec()).unwrap())) Ok(String::from(std::str::from_utf8(&m.to_vec()).unwrap()))
} }
pub fn rsa_sign(m: &str, p: &RsaPublicKey) -> Result<BigNum, ErrorStack> { pub fn rsa_sign(m: &str, p: &RsaPrivateKey) -> Result<BigNum, ErrorStack> {
let hash = sha256(m.as_bytes()); let hash = sha256(m.as_bytes());
let m = BigNum::from_slice(&hash)?; let m = BigNum::from_slice(&hash)?;
rsa_encrypt(&m, p) let m = rsa_padding_add_pkcs1(&m, p.n.num_bytes())?;
rsa_decrypt_unpadded(&m, p)
} }
pub fn rsa_verify(m: &str, p: &RsaPrivateKey, signature: &BigNum) -> Result<bool, ErrorStack> { pub fn rsa_verify(m: &str, p: &RsaPublicKey, signature: &BigNum) -> Result<bool, ErrorStack> {
let hash = BigNum::from_slice(&sha256(m.as_bytes()))?; let hash = BigNum::from_slice(&sha256(m.as_bytes()))?;
let m = rsa_decrypt(signature, p)?; let m = rsa_encrypt_unpadded(signature, p)?;
let m = rsa_padding_remove_pkcs1(&m, p.n.num_bytes())?;
Ok(m == hash) Ok(m == hash)
} }
pub fn rsa_verify_insecure( pub fn rsa_verify_insecure(
m: &str, m: &str,
p: &RsaPrivateKey, p: &RsaPublicKey,
signature: &BigNum, signature: &BigNum,
) -> Result<bool, ErrorStack> { ) -> Result<bool, ErrorStack> {
let hash = BigNum::from_slice(&sha256(m.as_bytes()))?; let hash = BigNum::from_slice(&sha256(m.as_bytes()))?;
const SHA256_HASH_LEN: usize = 32; const SHA256_HASH_LEN: usize = 32;
let pad_to = p.n.num_bytes(); let pad_to = p.n.num_bytes();
let m = rsa_decrypt_unpadded(signature, p)?; let m = rsa_encrypt_unpadded(signature, p)?;
println!("{:?}", m.to_vec());
assert!(m.num_bytes() + 1 == pad_to, "Padding length incorrect"); assert!(m.num_bytes() + 1 == pad_to, "Padding length incorrect");
// There was, 7 years ago, a common implementation flaw with RSA verifiers: they'd verify // There was, 7 years ago, a common implementation flaw with RSA verifiers: they'd verify
// signatures by "decrypting" them (cubing them modulo the public exponent) and then "parsing" // signatures by "decrypting" them (cubing them modulo the public exponent) and then "parsing"
// 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)?;
println!("{:?}", v);
let pad_to: usize = pad_to.try_into().unwrap();
assert!(v[0] == 0, "PKCS1 padding incorrect"); assert!(v[0] == 0, "PKCS1 padding incorrect");
assert!(v[1] == 1, "PKCS1 padding incorrect"); assert!(v[1] == 1, "PKCS1 padding incorrect");
assert!( let mut i = 2;
v[pad_to - SHA256_HASH_LEN - 2] == 0xff, while i < v.len() - 1 {
"PKCS1 padding incorrect" if v[i] == 0xff && v[i + 1] == 0x0 {
); break;
assert!( }
v[pad_to - SHA256_HASH_LEN - 1] == 0, i += 1;
"PKCS1 padding incorrect" }
); i += 2;
let sig = BigNum::from_slice(&v[pad_to - SHA256_HASH_LEN..])?; let sig = BigNum::from_slice(&v[i..i + SHA256_HASH_LEN])?;
Ok(sig == hash) Ok(sig == hash)
} }

View File

@ -43,11 +43,18 @@ pub fn challenge42() -> Option<()> {
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, &public_key).ok()?; let sig = rsa::rsa_sign(&m, &private_key).ok()?;
let sig_ok = rsa::rsa_verify(&m, &private_key, &sig).ok()?; let sig_ok = rsa::rsa_verify(&m, &public_key, &sig).ok()?;
assert!(sig_ok, "RSA verify does not work"); assert!(sig_ok, "RSA verify does not work");
assert!( assert!(
rsa::rsa_verify("other message", &private_key, &sig).ok()? == false, rsa::rsa_verify("other message", &public_key, &sig).ok()? == false,
"RSA verify does not work"
);
let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig).ok()?;
assert!(sig_ok, "RSA verify does not work");
assert!(
rsa::rsa_verify_insecure("other message", &public_key, &sig).ok()? == false,
"RSA verify does not work" "RSA verify does not work"
); );
@ -57,35 +64,37 @@ pub fn challenge42() -> Option<()> {
BigNum::from_slice(&b.to_bytes_be()) BigNum::from_slice(&b.to_bytes_be())
} }
fn _cube(n: &BigNum) -> BigNum {
n * &(n * n)
}
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 padding_str_len = 1024; let mut v = vec![0x0, 0x1, 0xff, 0x0];
let mut v = vec![0x0; 3 + padding_str_len];
v[0] = 0x0;
v[1] = 0x1;
for i in 2..padding_str_len + 2 {
v[i] = 0x0;
}
v[padding_str_len + 1] = 0xFF;
v[padding_str_len + 2] = 0x0;
v.append(&mut hash.to_vec()); v.append(&mut hash.to_vec());
while v.len() < 128 {
v.push(0);
}
// Add one to the cube root to ensure that when the number is
// cubed again it contains the desired signature.
let sig_cubed = BigNum::from_slice(&v)?; let sig_cubed = BigNum::from_slice(&v)?;
let sig = cube_root(&sig_cubed)?; let mut sig = cube_root(&sig_cubed)?;
sig.add_word(1)?;
Ok(sig) Ok(sig)
} }
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()?;
println!("i={i} c={c} m={m}");
assert_eq!(i, m, "rsa is broken");
let m = "hi mom"; let m = "hi mom";
let sig = rsa_fake_sign(&m).ok()?; let sig = rsa_fake_sign(&m).ok()?;
let sig_ok = rsa::rsa_verify_insecure(&m, &private_key, &sig).ok()?; let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig).ok()?;
assert!(sig_ok, "RSA verify does not work"); assert!(sig_ok, "RSA fake sign does not work");
println!("[xxxx] Challenge 42: Bleichenbacher's e=3 RSA Attack"); println!("[okay] Challenge 42: Bleichenbacher's e=3 RSA Attack");
Some(())
}
pub fn challenge43() -> Option<()> {
println!("[xxxx] Challenge 43: TBD");
Some(()) Some(())
} }