use std::io::BufReader;
use std::path::PathBuf;
use anyhow::{anyhow, Result};
use clap::Parser;
use openpgp_card::ocard::KeyType;
use openpgp_card_rpgp::CardSlot;
use pgp::composed::{Esk, Message, PlainSessionKey};
use crate::util;
use crate::util::DebugWrapper;
#[derive(Parser, Debug)]
pub struct DecryptCommand {
#[arg(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
ident: String,
#[arg(
name = "User PIN file",
short = 'p',
long = "user-pin",
help = "Optionally, get User PIN from a file"
)]
pin_file: Option<PathBuf>,
#[arg(name = "input")]
input: Option<PathBuf>,
#[arg(name = "output", long = "output", short = 'o')]
pub output: Option<PathBuf>,
}
pub fn decrypt(command: DecryptCommand) -> Result<(), Box<dyn std::error::Error>> {
let mut input = util::open_or_stdin(command.input.as_deref())?;
let (message, _headers) = Message::from_reader(BufReader::new(DebugWrapper(&mut input)))?;
let mut open = util::open_card(&command.ident)?;
let mut tx = open.transaction()?;
if tx.fingerprints()?.decryption().is_none() {
return Err(anyhow!("Can't decrypt: this card has no key in the decryption slot.").into());
}
let user_pin = util::get_pin(&mut tx, command.pin_file, crate::ENTER_USER_PIN)?;
let _ = util::verify_to_user(&mut tx, user_pin)?;
let cs = CardSlot::init_from_card(&mut tx, KeyType::Decryption, &|| {
eprintln!("Touch confirmation needed for decryption");
})?;
let Message::Encrypted { esk, .. } = &message else {
return Err(anyhow::anyhow!("message not encrypted").into());
};
for e in esk {
if let Esk::PublicKeyEncryptedSessionKey(pgp::packet::PublicKeyEncryptedSessionKey::V3 {
ref values,
..
}) = e
{
if let Ok((session_key, session_key_algorithm)) = cs.decrypt(values) {
let plain_session_key = PlainSessionKey::V3_4 {
key: session_key.into(),
sym_alg: session_key_algorithm,
};
let decrypted = message.decrypt_with_session_key(plain_session_key)?;
let data = unpack_unencrypted_msg(decrypted)?;
let mut sink = util::open_or_stdout(command.output.as_deref())?;
sink.write_all(&data)?;
return Ok(());
}
}
}
Err(anyhow::anyhow!("Couldn't decrypt message").into())
}
fn unpack_unencrypted_msg(mut msg: Message) -> Result<Vec<u8>> {
let mut i = 0;
while msg.is_compressed() && i < 3 {
msg = msg.decompress()?;
i += 1;
}
let cleartext = msg.as_data_vec()?;
Ok(cleartext)
}