use clap::Parser;
use cryptoy::attack::bleichenbacher::{AttackIter, Oracle};
use cryptoy::bytes::left_pad_0s;
use cryptoy::rsa::codec::Pkcs1V1_5;
use cryptoy::rsa::{codec, KeyPair, Rsa};
use num_bigint::BigUint;
use rand::{thread_rng, RngCore};
#[derive(Debug, Parser)]
pub struct Args {
#[arg(short)]
p: Option<BigUint>,
#[arg(short)]
q: Option<BigUint>,
#[arg(short, long, default_value = Some("128"))]
key_size: Option<usize>,
#[arg(short, long)]
min_padding: Option<usize>,
#[arg(short = 'M', long)]
message: String,
#[arg(long = "strong", default_value_t = false, action)]
strong_oracle: bool,
#[arg(long)]
seed: Option<u64>,
}
pub fn main() {
let args = Args::parse();
if args.key_size.is_none() && (args.p.is_none() || args.q.is_none()) {
panic!("Must provide either p and q or key_size");
}
let seed = match args.seed {
Some(s) => s,
None => {
let mut rng = thread_rng();
rng.next_u64()
}
};
println!("Seeding with {}", seed);
let key_pair = match args {
Args {
key_size: Some(b), ..
} => KeyPair::generate(b / 2, seed).unwrap(),
Args {
p: Some(p),
q: Some(q),
..
} => KeyPair::new(&p, &q).unwrap(),
_ => panic!("Key size or (p and q) must be specified"),
};
let modulus_bytes = key_pair.private_key.modulus.bits() / 8;
let min_padding: usize = args.min_padding.unwrap_or(8);
let codec = Pkcs1V1_5::new(
codec::BlockType::Type02,
modulus_bytes,
modulus_bytes - min_padding - 3,
seed,
);
let oracle = Oracle::new(key_pair.clone(), codec.clone(), args.strong_oracle);
let mut rsa = Rsa::<Pkcs1V1_5> {
codec: codec.clone(),
key_pair: key_pair.clone(),
};
let plaintext = args.message.as_bytes();
let ciphertext = rsa.encrypt(plaintext).unwrap();
let attack_iter = AttackIter::new(ciphertext, key_pair.public_key, oracle);
let mut oracle_calls = 0usize;
println!("0: {} possibilities", attack_iter.current_span());
for (m, state) in attack_iter {
println!(
"{}: {} possibilities ({} oracle calls)",
state.i,
state.span(),
state.oracle_calls
);
oracle_calls += state.oracle_calls;
if let Some(m) = m {
let bytes = left_pad_0s(&m.to_bytes_be(), codec.total_length);
let message_bytes = Pkcs1V1_5::strip_padding(&bytes).unwrap();
let message = String::from_utf8_lossy(&message_bytes);
println!("Decrypted: {}", m);
println!("Full bytes: {:?}", bytes);
println!("W/o padding: {:?}", message_bytes);
println!("Message: \"{}\"", message);
} else {
let upper = left_pad_0s(&state.upper_bound().to_bytes_be(), codec.total_length);
let lower = left_pad_0s(&state.lower_bound().to_bytes_be(), codec.total_length);
println!("Upper bound: {:?}", upper);
println!("Lower bound: {:?}", lower);
}
}
println!("Total oracle calls: {}", oracle_calls);
}