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, false)
}
pub fn decrypt_bytes_legacy(
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, true)
}
pub fn decrypt_with_key(
secret_key: &SignedSecretKey,
ciphertext: &[u8],
password: &str,
allow_legacy: bool,
) -> 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(|_| {
if !allow_legacy {
return Err(Error::Crypto("Decryption failed".to_string()));
}
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(|_| Error::Crypto("Decryption failed".to_string()))?;
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 {
}