diff --git a/Cargo.toml b/Cargo.toml index 13d3861..ee285c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] clap = { version = "4.5.38", features = ["derive"] } +hex = "0.4.3" rand = "0.9.1" rug = "1.27.0" diff --git a/src/main.rs b/src/main.rs index e49b34c..3fe5c0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ mod bootstrapping; mod dghv; mod dghv_asym; +mod fileutils; mod utils; use crate::dghv::{decrypt_bit, encrypt_bit, generate_secret_key}; -use std::path::{Path, PathBuf}; +use crate::dghv_asym::{encrypt_bit_asym, generate_keys}; +use crate::fileutils::{read_privkey, read_pubkey, write_privkey, write_pubkey}; +use clap::{ArgGroup, Args, Parser, Subcommand}; use rug::Integer; -use clap::{Parser, Subcommand}; - #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -25,26 +26,58 @@ enum Commands { size: u32, }, /// Encrypt a single bit - Encrypt { - /// bit to encrypt (0 or 1) - #[clap(short = 'b', long)] - bit: u8, - /// secret key (hex) - #[clap(short = 'k', long)] - key: String, + Encrypt(EncryptArgs), + /// Decrypt a single bit + Decrypt(DecryptArgs), + /// Generate pair (pubkey, privkey) + KeygenPair { + /// output path prefix for keys + #[arg(short = 'o', long)] + path: String, + /// security parameter eta (p bit-length) + #[clap(short = 's', long, default_value_t = 6400)] + size: u32, /// noise parameter #[clap(short = 'n', long, default_value_t = 80)] noise: u32, + /// number of public key elements (theta) + #[arg(short = 't', long, default_value_t = 10)] + theta: usize, }, - /// Decrypt a single bit - Decrypt { - /// ciphertext to decrypt (hex) - #[clap(short = 'c', long)] - ciphertext: String, - /// secret key (hex) - #[clap(short = 'k', long)] - key: String, - }, +} + +#[derive(Args)] +#[command(group(ArgGroup::new("keysource").required(true).args(&["key", "privkey", "pubkey"])))] +struct EncryptArgs { + /// bit to encrypt + #[arg(short = 'b', long)] + bit: u8, + /// Key as hex string + #[arg(short = 'k', long)] + key: Option, + /// path to private key file + #[arg(long)] + privkey: Option, + /// path to public key file + #[arg(long)] + pubkey: Option, + /// noise parameter + #[arg(short = 'n', long, default_value_t = 80)] + noise: u32, +} + +#[derive(Args)] +#[command(group(ArgGroup::new("keysource").required(true).args(&["key", "privkey"])))] +struct DecryptArgs { + /// ciphertext to decrypt (hex-encoded) + #[arg(short = 'c', long)] + ciphertext: String, + /// key as hex string + #[arg(short = 'k', long)] + key: Option, + /// path to private key file + #[arg(long)] + privkey: Option, } fn main() { @@ -52,22 +85,47 @@ fn main() { match cli.command { Commands::Keygen { size } => { - let sk = generate_secret_key(size as u32); + let sk = generate_secret_key(size); println!("Secret key (p): {}", sk.to_string_radix(16)); } - Commands::Encrypt { bit, key, noise } => { - let p = Integer::from_str_radix(&key, 16) - .expect("Invalid secret key: must be hexadecimal integer"); - let ct = encrypt_bit(bit, &p, noise as u32, noise as u32); - println!("Encrypted bit (c): {}", ct.to_string_radix(16)); + Commands::Encrypt(args) => { + let ct = if let Some(pub_path) = args.pubkey { + let pk = read_pubkey(pub_path).expect("Failed to load public key"); + encrypt_bit_asym(args.bit, &pk, args.noise) + } else { + let p = if let Some(hex_str) = args.key { + Integer::from_str_radix(&hex_str, 16).expect("Invalid key hex") + } else { + read_privkey(args.privkey.unwrap()) + .expect("Failed to load private key") + .p + }; + encrypt_bit(args.bit, &p, args.noise, args.noise) + }; + println!("Encrypted bit (hex): {}", ct.to_string_radix(16)); } - Commands::Decrypt { ciphertext, key } => { - let p = Integer::from_str_radix(&key, 16) - .expect("Invalid secret key: must be hexadecimal integer"); - let ct = Integer::from_str_radix(&ciphertext, 16) - .expect("Invalid ciphertext: must be hexadecimal integer"); + Commands::Decrypt(args) => { + let p = if let Some(hex_str) = args.key { + Integer::from_str_radix(&hex_str, 16).expect("Invalid key hex") + } else { + read_privkey(args.privkey.unwrap()) + .expect("Failed to load private key") + .p + }; + let ct = Integer::from_str_radix(&args.ciphertext, 16).expect("Invalid ciphertext"); let res = decrypt_bit(&ct, &p); println!("Decrypted bit: {}", res); } + Commands::KeygenPair { + path, + size, + noise, + theta, + } => { + let (sk, pk) = generate_keys(noise, size, noise, theta); + write_privkey(&path, &sk).expect("Failed to write privkey"); + write_pubkey(&path, &pk).expect("Failed to write pubkey"); + println!("keys written to {}.[privkey|pubkey]", path); + } } }