use magic_cap::err::MagicCapError;
use magic_cap::{
Immutable, ImmutableBuilder, ImmutableReadCap, ImmutableVerifier, ImmutableVerifyCap, ReadCap,
};
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
pub fn main_encrypt(
output: &mut impl Write,
plain_text: &Path,
output_fname: &Path,
) -> Result<(), MagicCapError> {
let mut input_file = std::fs::File::open(plain_text)?;
let output_file = File::create(output_fname)?;
let mut bufw = std::io::BufWriter::new(output_file);
let mut plaintext: Vec<u8> = vec![0u8; 4096];
let mut cryptor = ImmutableBuilder::new(4096, &mut bufw)?;
let mut r = input_file.read(&mut plaintext)?;
while r != 0 {
plaintext.resize(r, 0);
let _ = cryptor.write(&plaintext)?;
r = input_file.read(&mut plaintext)?;
}
let (cap, _) = cryptor.done()?;
let capstr = format!("{}", cap);
writeln!(output, "{}", capstr)?;
Ok(())
}
pub fn main_decrypt(
output: &mut impl Write,
cap: &str,
input_fname: &PathBuf,
outfile: &Path,
) -> Result<(), MagicCapError> {
let cap = ImmutableReadCap::try_from(cap)?;
let f = std::fs::File::open(input_fname)?;
let imm = Immutable::read(&mut std::io::BufReader::new(f))?;
match cap.decrypt(&imm) {
Ok(plain) => {
let mut out = std::fs::File::create(outfile)?;
out.write_all(plain.as_slice())?;
match outfile.to_str() {
Some(of) => {
writeln!(
output,
"Wrote {} bytes of plaintext to \"{}\".",
plain.len(),
of,
)?;
Ok(())
}
None => Ok(()),
}
}
Err(e) => match &e {
MagicCapError::McapMetadataDiscordant() => Err(e),
_ => {
writeln!(output, "Error decrypting: {}", e)?;
Ok(())
}
},
}
}
pub fn main_verify(cap: &str, input_fname: &Path) -> Result<(), MagicCapError> {
let cap = ImmutableVerifyCap::try_from(cap)?;
let f = std::fs::File::open(input_fname)?;
let imm = Immutable::read(&mut std::io::BufReader::new(f))?;
cap.verify(&imm)?;
Ok(())
}
pub fn main_reduce(output: &mut impl Write, cap: &str) -> Result<(), MagicCapError> {
if let Ok(readcap) = ImmutableReadCap::try_from(cap) {
let verifycap = ImmutableVerifyCap::from(readcap);
writeln!(output, "{}", verifycap)?;
} else if let Ok(verifycap) = ImmutableVerifyCap::try_from(cap) {
writeln!(output, "{}", verifycap)?;
} else {
writeln!(output, "Unknown kind of cap.")?;
return Err(MagicCapError::InvalidCap(cap.to_string()));
}
Ok(())
}
#[cfg(test)]
pub mod test {
use super::*;
use proptest::prelude::*;
use tempfile::tempdir;
proptest! {
#[test]
fn round_trip_main(s in "\\PC+") {
let outd = tempdir()?;
let plain = outd.path().join("plain");
{
let mut tmp = File::create(&plain)?;
tmp.write(s.as_bytes())?;
} let cipher = outd.path().join("cipher");
let mut output = vec!();
main_encrypt(&mut output, &plain, &cipher).unwrap();
let capstr: &str = std::str::from_utf8(&output)?.trim_end();
let round = outd.path().join("decrypted");
let mut output = vec!();
main_reduce(&mut output, capstr)?;
let verifycap = std::str::from_utf8(&output)?.trim_end();
main_verify(verifycap, &cipher).unwrap();
let mut output = vec!();
main_decrypt(&mut output, capstr, &cipher, &round).unwrap();
let mut og = String::new();
let mut other = String::new();
File::open(plain)?.read_to_string(&mut og)?;
File::open(round)?.read_to_string(&mut other)?;
assert_eq!(og, other);
}
}
}