use std::io::{Cursor, Read};
use std::path::Path;
use pgp::composed::{Message, SignedSecretKey};
use pgp::types::Password;
use crate::error::{Error, Result};
use crate::internal::parse_secret_key;
pub fn decrypt_bytes(secret_cert: &[u8], ciphertext: &[u8], password: &str) -> Result<Vec<u8>> {
let secret_key = parse_secret_key(secret_cert)?;
decrypt_with_key(&secret_key, ciphertext, password)
}
pub fn decrypt_with_key(
secret_key: &SignedSecretKey,
ciphertext: &[u8],
password: &str,
) -> Result<Vec<u8>> {
let password: Password = password.into();
let message = match Message::from_armor(Cursor::new(ciphertext)) {
Ok((msg, _headers)) => msg,
Err(_) => Message::from_bytes(ciphertext)
.map_err(|e| Error::Parse(e.to_string()))?,
};
let decrypted = message.decrypt(&password, secret_key)
.or_else(|_| {
let msg = match Message::from_armor(Cursor::new(ciphertext)) {
Ok((m, _headers)) => m,
Err(_) => Message::from_bytes(ciphertext)
.map_err(|e| Error::Parse(e.to_string()))?,
};
msg.decrypt_legacy(&password, secret_key)
.map_err(|e| Error::Crypto(e.to_string()))
})
.map_err(|e: Error| {
if e.to_string().contains("password") || e.to_string().contains("decrypt") {
Error::InvalidPassword
} else {
e
}
})?;
let mut decompressed = if decrypted.is_compressed() {
decrypted.decompress()
.map_err(|e| Error::Crypto(e.to_string()))?
} else {
decrypted
};
decompressed.as_data_vec()
.map_err(|e| Error::Crypto(e.to_string()))
}
pub fn decrypt_file(
secret_cert: &[u8],
input: impl AsRef<Path>,
output: impl AsRef<Path>,
password: &str,
) -> Result<()> {
let ciphertext = std::fs::read(input.as_ref())?;
let plaintext = decrypt_bytes(secret_cert, &ciphertext, password)?;
std::fs::write(output.as_ref(), plaintext)?;
Ok(())
}
pub fn decrypt_reader_to_file<R: Read>(
secret_cert: &[u8],
mut reader: R,
output: impl AsRef<Path>,
password: &str,
) -> Result<()> {
let mut ciphertext = Vec::new();
reader.read_to_end(&mut ciphertext)?;
let plaintext = decrypt_bytes(secret_cert, &ciphertext, password)?;
std::fs::write(output.as_ref(), plaintext)?;
Ok(())
}
#[cfg(test)]
mod tests {
}