cryptoy 0.4.0

Toy implementations of cryptographic protocols for educational purposes
Documentation
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"))]
    /// Overrides p and q if specified and generates a random prime pair totalling this number of
    /// bits
    key_size: Option<usize>,

    #[arg(short, long)]
    /// Minimum number of random bytes to pad the message with in PKCS
    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 encoded_plaintext = rsa.codec.encode(plaintext).unwrap();

    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);
}