diff --git a/src/main.rs b/src/main.rs index 1bf6e87..cf5fc4f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,4 +70,5 @@ fn main() { 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::challenge43().unwrap_or_else(|| println!("[fail] challenge 43")); } diff --git a/src/rsa.rs b/src/rsa.rs index 4826b65..5bd5d94 100644 --- a/src/rsa.rs +++ b/src/rsa.rs @@ -210,46 +210,45 @@ pub fn rsa_decrypt_str(c: &BigNum, p: &RsaPrivateKey) -> Result Result { +pub fn rsa_sign(m: &str, p: &RsaPrivateKey) -> Result { let hash = sha256(m.as_bytes()); 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 { +pub fn rsa_verify(m: &str, p: &RsaPublicKey, signature: &BigNum) -> Result { 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) } pub fn rsa_verify_insecure( m: &str, - p: &RsaPrivateKey, + p: &RsaPublicKey, signature: &BigNum, ) -> Result { let hash = BigNum::from_slice(&sha256(m.as_bytes()))?; const SHA256_HASH_LEN: usize = 32; let pad_to = p.n.num_bytes(); - let m = rsa_decrypt_unpadded(signature, p)?; - println!("{:?}", m.to_vec()); + let m = rsa_encrypt_unpadded(signature, p)?; 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 // signatures by "decrypting" them (cubing them modulo the public exponent) and then "parsing" // them by looking for 00h 01h ... ffh 00h ASN.1 HASH. 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[1] == 1, "PKCS1 padding incorrect"); - assert!( - v[pad_to - SHA256_HASH_LEN - 2] == 0xff, - "PKCS1 padding incorrect" - ); - assert!( - v[pad_to - SHA256_HASH_LEN - 1] == 0, - "PKCS1 padding incorrect" - ); - let sig = BigNum::from_slice(&v[pad_to - SHA256_HASH_LEN..])?; + let mut i = 2; + while i < v.len() - 1 { + if v[i] == 0xff && v[i + 1] == 0x0 { + break; + } + i += 1; + } + i += 2; + let sig = BigNum::from_slice(&v[i..i + SHA256_HASH_LEN])?; Ok(sig == hash) } diff --git a/src/set6.rs b/src/set6.rs index df2f97d..b253616 100644 --- a/src/set6.rs +++ b/src/set6.rs @@ -43,11 +43,18 @@ pub fn challenge42() -> Option<()> { assert_eq!(i, m, "rsa is broken"); let m = "a message to verify"; - let sig = rsa::rsa_sign(&m, &public_key).ok()?; - let sig_ok = rsa::rsa_verify(&m, &private_key, &sig).ok()?; + let sig = rsa::rsa_sign(&m, &private_key).ok()?; + let sig_ok = rsa::rsa_verify(&m, &public_key, &sig).ok()?; assert!(sig_ok, "RSA verify does not work"); 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" ); @@ -57,35 +64,37 @@ pub fn challenge42() -> Option<()> { BigNum::from_slice(&b.to_bytes_be()) } + fn _cube(n: &BigNum) -> BigNum { + n * &(n * n) + } + pub fn rsa_fake_sign(m: &str) -> Result { let hash = sha256(m.as_bytes()); - let padding_str_len = 1024; - - 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; + let mut v = vec![0x0, 0x1, 0xff, 0x0]; 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 = cube_root(&sig_cubed)?; + let mut sig = cube_root(&sig_cubed)?; + sig.add_word(1)?; + 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 sig = rsa_fake_sign(&m).ok()?; - let sig_ok = rsa::rsa_verify_insecure(&m, &private_key, &sig).ok()?; - assert!(sig_ok, "RSA verify does not work"); + let sig_ok = rsa::rsa_verify_insecure(&m, &public_key, &sig).ok()?; + 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(()) }