diff --git a/src/fileutils.rs b/src/fileutils.rs new file mode 100644 index 0000000..cec40da --- /dev/null +++ b/src/fileutils.rs @@ -0,0 +1,65 @@ +use crate::dghv_asym::{PrivateKey, PublicKey}; +use rug::Integer; +use std::fs::{read_to_string, File}; +use std::io::{self, Read, Write}; +use std::path::Path; + +pub fn write_privkey>(prefix: P, sk: &PrivateKey) -> io::Result<()> { + let mut file = File::create(prefix.as_ref().with_extension("privkey"))?; + let mut hex = sk.p.to_string_radix(16); + if hex.len() % 2 != 0 { + hex.insert(0, '0'); + } + writeln!(file, "{}", hex)?; + Ok(()) +} + +pub fn write_pubkey>(prefix: P, pk: &PublicKey) -> io::Result<()> { + let mut file = File::create(prefix.as_ref().with_extension("pubkey"))?; + for x in &pk.xs { + let mut hex = x.to_string_radix(16); + if hex.len() % 2 != 0 { + hex.insert(0, '0'); + } + writeln!(file, "{}", hex)?; + } + Ok(()) +} + +pub fn read_privkey>(path: P) -> io::Result { + let hex = std::fs::read_to_string(path)?; + let hex = hex.trim(); + let p = Integer::from_str_radix(hex, 16) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + Ok(PrivateKey { p }) +} + +pub fn read_pubkey>(path: P) -> io::Result { + let mut xs = Vec::new(); + let content = { + let mut s = String::new(); + File::open(path)?.read_to_string(&mut s)?; + s + }; + for line in content.lines() { + // each line is a hex-encoded key + let bytes = + hex::decode(line.trim()).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let hex = hex::encode(bytes); + let x = Integer::from_str_radix(&hex, 16) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + xs.push(x); + } + Ok(PublicKey { xs }) +} + +pub fn read_hex_from_file>(path: P) -> io::Result { + let content = read_to_string(path)?; + Ok(content.trim().to_string()) +} + +pub fn write_hex_to_file>(path: P, hex_str: &str) -> io::Result<()> { + let mut file = File::create(path)?; + writeln!(file, "{}", hex_str)?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 3fe5c0f..095b51f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,9 @@ mod fileutils; mod utils; use crate::dghv::{decrypt_bit, encrypt_bit, generate_secret_key}; use crate::dghv_asym::{encrypt_bit_asym, generate_keys}; -use crate::fileutils::{read_privkey, read_pubkey, write_privkey, write_pubkey}; +use crate::fileutils::{ + read_hex_from_file, read_privkey, read_pubkey, write_hex_to_file, write_privkey, write_pubkey, +}; use clap::{ArgGroup, Args, Parser, Subcommand}; use rug::Integer; @@ -64,14 +66,21 @@ struct EncryptArgs { /// noise parameter #[arg(short = 'n', long, default_value_t = 80)] noise: u32, + /// optional output file for ciphertext (hex-encoded) + #[arg(short = 'o', long)] + output: Option, } #[derive(Args)] #[command(group(ArgGroup::new("keysource").required(true).args(&["key", "privkey"])))] +#[command(group(ArgGroup::new("cipher_source").required(true).args(&["ciphertext", "file"])))] struct DecryptArgs { - /// ciphertext to decrypt (hex-encoded) + /// ciphertext hex string #[arg(short = 'c', long)] - ciphertext: String, + ciphertext: Option, + /// path to ciphertext file + #[arg(short = 'f', long)] + file: Option, /// key as hex string #[arg(short = 'k', long)] key: Option, @@ -102,9 +111,21 @@ fn main() { }; encrypt_bit(args.bit, &p, args.noise, args.noise) }; - println!("Encrypted bit (hex): {}", ct.to_string_radix(16)); + let hexct = ct.to_string_radix(16); + if let Some(outfile) = args.output { + write_hex_to_file(outfile, &hexct).expect("Failed to write ciphertext to file"); + } else { + println!("Encrypted bit (hex): {}", hexct); + } } Commands::Decrypt(args) => { + let ct_hex = if let Some(hex_str) = args.ciphertext { + hex_str + } else { + read_hex_from_file(args.file.clone().unwrap()) + .expect("Failed to read ciphertext file") + }; + let ct = Integer::from_str_radix(&ct_hex, 16).expect("Invalid ciphertext"); let p = if let Some(hex_str) = args.key { Integer::from_str_radix(&hex_str, 16).expect("Invalid key hex") } else { @@ -112,7 +133,6 @@ fn main() { .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); }