Files
oss-backupd/src/pgp_util.rs
2019-12-01 11:15:55 +08:00

106 lines
3.0 KiB
Rust

extern crate sequoia_openpgp as openpgp;
use crate::openpgp::armor;
use std::{
fs::File,
path::Path,
io::{
ErrorKind,
Read,
Write,
BufWriter,
},
};
use rust_util::{
XResult,
new_box_error,
};
use openpgp::{
types::KeyFlags,
TPK,
parse::Parse,
serialize::stream::{
Recipient,
Message,
Encryptor,
LiteralWriter,
},
};
use indicatif::{
ProgressBar,
ProgressStyle
};
const BUFF_SIZE: usize = 512 * 1024;
const PB_PROGRESS: &str = "#-";
const PB_TEMPLATE: &str = "{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})";
pub struct OpenPGPTool {
pub tpk: TPK,
}
impl OpenPGPTool {
pub fn from_file(file: &str) -> XResult<OpenPGPTool> {
Ok(OpenPGPTool{
tpk: TPK::from_file(std::path::Path::new(file))?,
})
}
pub fn from_bytes(bs: &[u8]) -> XResult<OpenPGPTool> {
Ok(OpenPGPTool{
tpk: TPK::from_bytes(&bs)?,
})
}
pub fn encrypt_file(&self, from_file: &str, to_file: &str, armor: bool) -> XResult<()> {
if !Path::new(from_file).exists() {
return Err(new_box_error(&format!("From file NOT exists: {}", from_file)));
}
if Path::new(to_file).exists() {
return Err(new_box_error(&format!("To file exists: {}", to_file)));
}
let recipient: Recipient = match self.tpk.keys_valid()
.key_flags(KeyFlags::default().set_encrypt_at_rest(true).set_encrypt_for_transport(true))
.map(|(_, _, key)| key.into())
.nth(0) {
None => return Err(new_box_error("Encryption key not found in TPK")),
Some(r) => r,
};
let bw = BufWriter::new(File::create(to_file)?);
let message = if armor {
Message::new(armor::Writer::new(bw, armor::Kind::Message, &[])?)
} else {
Message::new(bw)
};
let encryptor = Encryptor::for_recipient(message, recipient).build()?;
let mut pgp_encrypt_writer = LiteralWriter::new(encryptor).build()?;
let mut from = File::open(from_file)?;
encrypt_read_write(&mut from, &mut pgp_encrypt_writer)?;
pgp_encrypt_writer.finalize()?;
Ok(())
}
}
fn encrypt_read_write(file: &mut File, write: &mut dyn Write) -> XResult<()> {
let mut buf: [u8; BUFF_SIZE] = [0u8; BUFF_SIZE];
let file_len = file.metadata()?.len();
let mut read = 0_u64;
let pb = ProgressBar::new(file_len);
pb.set_style(ProgressStyle::default_bar().template(PB_TEMPLATE).progress_chars(PB_PROGRESS));
loop {
let len = match file.read(&mut buf) {
Ok(0) => { pb.finish_and_clear(); return Ok(()); },
Ok(len) => len,
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(Box::new(e)),
};
write.write(&buf[..len])?;
read += len as u64;
pb.set_position(read);
}
}