Finish challenge set 6, original challenges completed
This commit is contained in:
19
src/main.rs
19
src/main.rs
@@ -1,10 +1,10 @@
|
||||
#![warn(clippy::pedantic)]
|
||||
#![warn(clippy::nursery)]
|
||||
#![allow(clippy::cast_possible_truncation)]
|
||||
#![allow(clippy::cast_precision_loss)]
|
||||
#![allow(clippy::items_after_statements)]
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![feature(string_remove_matches)]
|
||||
mod bytes;
|
||||
mod bytes_base64;
|
||||
mod cbc;
|
||||
@@ -28,7 +28,7 @@ mod srp;
|
||||
mod utils;
|
||||
|
||||
fn main() {
|
||||
const RUN_ALL: bool = false;
|
||||
const RUN_ALL: bool = true;
|
||||
if RUN_ALL {
|
||||
set1::challenge1();
|
||||
set1::challenge2();
|
||||
@@ -70,12 +70,13 @@ 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::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::challenge48().unwrap_or_else(|_| println!("[fail] challenge 48"));
|
||||
}
|
||||
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"));
|
||||
}
|
||||
|
||||
227
src/set6.rs
227
src/set6.rs
@@ -2,10 +2,8 @@ use crate::bytes::Bytes;
|
||||
use crate::bytes_base64::BytesBase64;
|
||||
use crate::dsa;
|
||||
use crate::rsa;
|
||||
use crate::utils::bnclone;
|
||||
use num_bigint::BigUint;
|
||||
use openssl::bn::BigNum;
|
||||
use openssl::bn::BigNumContext;
|
||||
use crate::utils::{bn, bnclone, cube_root, div_ceil, div_floor};
|
||||
use openssl::bn::{BigNum, BigNumContext};
|
||||
use openssl::error::ErrorStack;
|
||||
use openssl::sha::sha256;
|
||||
|
||||
@@ -61,12 +59,6 @@ pub fn challenge42() -> Result<(), ErrorStack> {
|
||||
"RSA verify does not work"
|
||||
);
|
||||
|
||||
fn cube_root(n: &BigNum) -> Result<BigNum, ErrorStack> {
|
||||
let b = BigUint::from_bytes_be(&n.to_vec());
|
||||
let b = b.nth_root(3);
|
||||
BigNum::from_slice(&b.to_bytes_be())
|
||||
}
|
||||
|
||||
fn _cube(n: &BigNum) -> BigNum {
|
||||
n * &(n * n)
|
||||
}
|
||||
@@ -82,7 +74,7 @@ pub fn challenge42() -> Result<(), ErrorStack> {
|
||||
// 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 mut sig = cube_root(&sig_cubed)?;
|
||||
let mut sig = cube_root(&sig_cubed);
|
||||
sig.add_word(1)?;
|
||||
|
||||
Ok(sig)
|
||||
@@ -192,12 +184,12 @@ pub mod challenge44 {
|
||||
let file = std::fs::File::open("data/44.txt").unwrap();
|
||||
let mut result = Vec::new();
|
||||
let mut lines: Vec<String> = BufReader::new(file).lines().map(|l| l.unwrap()).collect();
|
||||
// each message cosists of four lines: msg, s, r, m (sha1 hash of msg)
|
||||
for line in lines.chunks_mut(4) {
|
||||
line[0].remove_matches("msg: ");
|
||||
line[1].remove_matches("s: ");
|
||||
line[2].remove_matches("r: ");
|
||||
line[3].remove_matches("m: ");
|
||||
// each message cosists of four lines: msg, s, r, m (sha1 hash of msg)
|
||||
line[0] = line[0][5..].to_string();
|
||||
line[1] = line[1][3..].to_string();
|
||||
line[2] = line[2][3..].to_string();
|
||||
line[3] = line[3][3..].to_string();
|
||||
let msg = Bytes::from_utf8(&line[0]);
|
||||
let s = BigNum::from_dec_str(&line[1]).unwrap();
|
||||
let r = BigNum::from_dec_str(&line[2]).unwrap();
|
||||
@@ -205,7 +197,7 @@ pub mod challenge44 {
|
||||
assert_eq!(
|
||||
dsa::h(&msg).unwrap(),
|
||||
m,
|
||||
"Message hash from data/44.txt does not match"
|
||||
"hashes in data/44.txt do not match"
|
||||
);
|
||||
let d = DsaSignedMsg { msg, r, s, m };
|
||||
result.push(d);
|
||||
@@ -407,20 +399,136 @@ pub fn challenge46() -> Result<(), ErrorStack> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod challenge47 {
|
||||
use crate::rsa;
|
||||
use crate::utils::{bn, bnclone, div_ceil, div_floor};
|
||||
use openssl::bn::{BigNum, BigNumContext};
|
||||
use std::cmp;
|
||||
|
||||
pub fn bleichenbachers_pkcs15_padding_oracle_attack(
|
||||
c: &BigNum,
|
||||
e: &BigNum,
|
||||
d: &BigNum,
|
||||
n: &BigNum,
|
||||
) -> BigNum {
|
||||
#![allow(non_snake_case)]
|
||||
let mut ctx = BigNumContext::new().unwrap();
|
||||
|
||||
let public_key = rsa::RsaPublicKey {
|
||||
e: bnclone(e),
|
||||
n: bnclone(n),
|
||||
};
|
||||
|
||||
let private_key = rsa::RsaPrivateKey {
|
||||
d: bnclone(d),
|
||||
n: bnclone(n),
|
||||
};
|
||||
|
||||
// k is number of bytes of n as specified in Bleichenbacher's paper
|
||||
let n_bytes = n.num_bytes();
|
||||
let k: u32 = n_bytes.try_into().unwrap();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
let mul = |s: &BigNum| -> BigNum {
|
||||
let mut ctx = BigNumContext::new().unwrap();
|
||||
let mut f = BigNum::new().unwrap();
|
||||
f.mod_exp(s, &public_key.e, &n, &mut ctx).unwrap();
|
||||
&(c * &f) % n
|
||||
};
|
||||
|
||||
let mut B = BigNum::new().unwrap();
|
||||
// B = 2^(8(k−2))
|
||||
B.exp(&bn(2), &(&bn(8) * &(&bn(k) - &bn(2))), &mut ctx)
|
||||
.unwrap();
|
||||
|
||||
// Step 1: Blinding (not necessary because already PKCS conforming).
|
||||
let mut i: u32 = 1;
|
||||
let mut s: BigNum = bn(1);
|
||||
let mut m: Vec<(BigNum, BigNum)> = vec![(&bn(2) * &B, &(&bn(3) * &B) - &bn(1))];
|
||||
let solution: BigNum;
|
||||
|
||||
// Step 2: Searching for PKCS conforming messages.
|
||||
loop {
|
||||
if i == 1 {
|
||||
// Step 2.a: Starting the search.
|
||||
s = div_ceil(&n, &(&bn(3) * &B));
|
||||
while !oracle(&mul(&s)) {
|
||||
s.add_word(1).unwrap();
|
||||
}
|
||||
} else if m.len() > 1 {
|
||||
// Step 2.b: Searching with more than one interval left.
|
||||
s.add_word(1).unwrap();
|
||||
while !oracle(&mul(&s)) {
|
||||
s.add_word(1).unwrap();
|
||||
}
|
||||
} else if m.len() == 1 {
|
||||
// Step 2.c: Searching with one interval left.
|
||||
let (a, b) = (bnclone(&m[0].0), bnclone(&m[0].1));
|
||||
let mut found = false;
|
||||
let mut r = div_ceil(&(&bn(2) * &(&(&b * &s) - &(&bn(2) * &(B)))), &n);
|
||||
while !found {
|
||||
let mut s_lower = div_ceil(&(&(&bn(2) * &B) + &(&r * n)), &b);
|
||||
let s_upper = div_ceil(&(&(&bn(3) * &B) + &(&r * n)), &a);
|
||||
while s_lower < s_upper {
|
||||
if oracle(&mul(&s_lower)) {
|
||||
found = true;
|
||||
s = s_lower;
|
||||
break;
|
||||
}
|
||||
s_lower.add_word(1).unwrap();
|
||||
}
|
||||
r.add_word(1).unwrap();
|
||||
}
|
||||
} else {
|
||||
panic!("step 2 -- no more intervals?");
|
||||
}
|
||||
|
||||
// Step 3: Narrowing the set of solutions.
|
||||
let mut m_new: Vec<(BigNum, BigNum)> = vec![];
|
||||
for (a, b) in m {
|
||||
let lower_ceil = div_ceil(&(&(&a * &s) - &(&(&bn(3) * &B) + &bn(1))), &n);
|
||||
let mut upper_ceil = div_ceil(&(&(&b * &s) - &(&bn(2) * &B)), &n);
|
||||
let upper_floor = div_floor(&(&(&b * &s) - &(&bn(2) * &B)), &n);
|
||||
if upper_floor == upper_ceil {
|
||||
upper_ceil.add_word(1).unwrap();
|
||||
}
|
||||
|
||||
let mut r = lower_ceil;
|
||||
while r < upper_ceil {
|
||||
let a_new = cmp::max(bnclone(&a), div_ceil(&(&(&bn(2) * &B) + &(&r * n)), &s));
|
||||
let b_new = cmp::min(
|
||||
bnclone(&b),
|
||||
div_floor(&(&(&(&bn(3) * &B) - &bn(1)) + &(&r * n)), &s),
|
||||
);
|
||||
m_new.push((a_new, b_new));
|
||||
r.add_word(1).unwrap();
|
||||
}
|
||||
}
|
||||
m = m_new;
|
||||
|
||||
// Step 4: Computing the solutions.
|
||||
if m.len() == 1 && m[0].0 == m[0].1 {
|
||||
solution = rsa::rsa_padding_remove_pkcs1(&m[0].0, n_bytes).unwrap();
|
||||
return solution;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)?;
|
||||
println!("e={:?}", public_key.e);
|
||||
println!("d={:?}", private_key.d);
|
||||
println!("n={:?}", private_key.n);
|
||||
|
||||
// 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();
|
||||
println!("m={:?}", m);
|
||||
println!("n_bytes={}", n_bytes);
|
||||
let m = BigNum::from_slice(&"kick it, CC".as_bytes())?;
|
||||
let c = rsa::rsa_encrypt(&m, &public_key)?;
|
||||
let n_bytes = private_key.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.
|
||||
@@ -430,24 +538,57 @@ pub fn challenge47() -> Result<(), ErrorStack> {
|
||||
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)?;
|
||||
println!("c={:?}", c);
|
||||
assert!(oracle(&c), "c is padded");
|
||||
assert!(div_floor(&bn(3), &bn(2)) == bn(1));
|
||||
assert!(div_ceil(&bn(3), &bn(2)) == bn(2));
|
||||
assert!(div_floor(&bn(4), &bn(2)) == bn(2));
|
||||
assert!(div_ceil(&bn(4), &bn(2)) == bn(2));
|
||||
|
||||
assert!(!oracle(&c_unpadded), "oracle wrongly thinks unpadded message is padded");
|
||||
assert!(oracle(&c), "oracle wrongly thinks padded message is not padded");
|
||||
let solution = challenge47::bleichenbachers_pkcs15_padding_oracle_attack(
|
||||
&c,
|
||||
&public_key.e,
|
||||
&private_key.d,
|
||||
&private_key.n,
|
||||
);
|
||||
|
||||
// B = 2^(8(k−2)); k is the length of n in bytes;
|
||||
// let mut ctx = BigNumContext::new()?;
|
||||
// let mut p = BigNum::new()?;
|
||||
// let k = BigNum::from_u32(n_bytes.try_into().unwrap())?;
|
||||
// p.checked_sub(&k, &BigNum::from_u32(2)?);
|
||||
// p = &p * BigNum::from_u32(8);
|
||||
|
||||
// b.exp(&BigNum::from_u32(2)?, &BigNum::from_u32(8)? * &(&n_bytes - &BigNum::from_u32(2)), &mut ctx);
|
||||
|
||||
|
||||
println!("[xxxx] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)");
|
||||
assert_eq!(m, solution, "Bleichenbacher's attack failed");
|
||||
println!("[okay] Challenge 47: Bleichenbacher's PKCS 1.5 Padding Oracle (Simple Case)");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn challenge48() -> Result<(), ErrorStack> {
|
||||
// Set yourself up the way you did in #47, but this time generate a 768 bit modulus.
|
||||
|
||||
// Use e, d, and n that execute step 2.b. (not all 768 bit modulus do)
|
||||
let public_key = rsa::RsaPublicKey {
|
||||
e: BigNum::from_dec_str("3").unwrap(),
|
||||
n: BigNum::from_dec_str(
|
||||
"11154339647448643581126395602431513296069677965376957820612905826953621832839",
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let private_key = rsa::RsaPrivateKey {
|
||||
d: BigNum::from_dec_str(
|
||||
"7436226431632429054084263734954342197144411188982219770498201348078527629483",
|
||||
)
|
||||
.unwrap(),
|
||||
n: BigNum::from_dec_str(
|
||||
"11154339647448643581126395602431513296069677965376957820612905826953621832839",
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let m = BigNum::from_slice(&"kick it, CC".as_bytes())?;
|
||||
let c = rsa::rsa_encrypt(&m, &public_key)?;
|
||||
let solution = challenge47::bleichenbachers_pkcs15_padding_oracle_attack(
|
||||
&c,
|
||||
&public_key.e,
|
||||
&private_key.d,
|
||||
&private_key.n,
|
||||
);
|
||||
assert_eq!(m, solution, "Bleichenbacher's attack failed");
|
||||
|
||||
println!("[okay] Challenge 48: Bleichenbacher's PKCS 1.5 Padding Oracle (Complex Case)");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
35
src/utils.rs
35
src/utils.rs
@@ -1,5 +1,6 @@
|
||||
use crate::{bytes::Bytes, bytes_base64::BytesBase64};
|
||||
use openssl::bn::BigNum;
|
||||
use num_bigint::BigUint;
|
||||
use openssl::bn::{BigNum, BigNumContext};
|
||||
use std::{
|
||||
io::{BufRead, BufReader},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
@@ -55,3 +56,35 @@ pub fn xor(a: &[u8], b: &[u8]) -> Vec<u8> {
|
||||
pub fn bnclone(b: &BigNum) -> BigNum {
|
||||
BigNum::from_slice(&b.to_vec()).unwrap()
|
||||
}
|
||||
|
||||
pub fn cube_root(n: &BigNum) -> BigNum {
|
||||
let b = BigUint::from_bytes_be(&n.to_vec());
|
||||
let b = b.nth_root(3);
|
||||
BigNum::from_slice(&b.to_bytes_be()).unwrap()
|
||||
}
|
||||
|
||||
pub fn bn(n: u32) -> BigNum {
|
||||
BigNum::from_u32(n).unwrap()
|
||||
}
|
||||
|
||||
pub fn div_rem(a: &BigNum, b: &BigNum) -> (BigNum, BigNum) {
|
||||
// kReturns the divider and remainder of a / b.
|
||||
let mut ctx = BigNumContext::new().unwrap();
|
||||
let mut div = BigNum::new().unwrap();
|
||||
let mut rem = BigNum::new().unwrap();
|
||||
div.div_rem(&mut rem, a, b, &mut ctx).unwrap();
|
||||
(div, rem)
|
||||
}
|
||||
|
||||
pub fn div_floor(a: &BigNum, b: &BigNum) -> BigNum {
|
||||
let (div, _) = div_rem(a, b);
|
||||
div
|
||||
}
|
||||
|
||||
pub fn div_ceil(a: &BigNum, b: &BigNum) -> BigNum {
|
||||
let (mut div, rem) = div_rem(a, b);
|
||||
if rem.num_bits() != 0 {
|
||||
div.add_word(1).unwrap();
|
||||
}
|
||||
div
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user