rsop-oct 0.1.3

SOP CLI tool for OpenPGP card devices based on rPGP
Documentation
// SPDX-FileCopyrightText: Heiko Schaefer <heiko@schaefer.name>
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::io;
use std::io::BufReader;
use std::time::SystemTime;

use pgp::composed::{Esk, Message, PlainSessionKey};
use pgp::types::PublicKeyTrait;
use rpgpie::certificate::Certificate;
use sop::errors::Error;

use crate::card::get_card;
use crate::util::DebugWrapper;
use crate::{card, util, Certs, Keys, RPGSOPOCT};

#[derive(Default)]
pub(crate) struct Decrypt {
    verify: Vec<Certificate>,
    _session_keys: Vec<sop::SessionKey>,
    decryption_keys: Vec<Certificate>,
    key_passwords: Vec<sop::Password>, // Passwords for asymmetric component key material
    skesk_passwords: Vec<sop::Password>,
}

impl Decrypt {
    pub(crate) fn new() -> Self {
        Default::default()
    }
}

impl<'a> sop::ops::Decrypt<'a, RPGSOPOCT, Certs, Keys> for Decrypt {
    fn verify_not_before(
        self: Box<Self>,
        _t: SystemTime,
    ) -> Box<dyn sop::ops::Decrypt<'a, RPGSOPOCT, Certs, Keys> + 'a> {
        todo!();

        // self.verify.not_before = Some(t);
        // self
    }

    fn verify_not_after(
        self: Box<Self>,
        _t: SystemTime,
    ) -> Box<dyn sop::ops::Decrypt<'a, RPGSOPOCT, Certs, Keys> + 'a> {
        todo!()

        // self.verify.not_after = Some(t);
        // self
    }

    fn verify_with_certs(
        mut self: Box<Self>,
        certs: &Certs,
    ) -> sop::Result<Box<dyn sop::ops::Decrypt<'a, RPGSOPOCT, Certs, Keys> + 'a>> {
        certs.certs.iter().for_each(|c| self.verify.push(c.clone()));
        Ok(self)
    }

    fn with_session_key(
        self: Box<Self>,
        _session_key: sop::SessionKey,
    ) -> sop::Result<Box<dyn sop::ops::Decrypt<'a, RPGSOPOCT, Certs, Keys> + 'a>> {
        todo!()

        // self.session_keys.push(sk);
        // Ok(self)
    }

    fn with_password(
        mut self: Box<Self>,
        password: sop::Password,
    ) -> sop::Result<Box<dyn sop::ops::Decrypt<'a, RPGSOPOCT, Certs, Keys> + 'a>> {
        self.skesk_passwords.push(password);
        Ok(self)
    }

    fn with_keys(
        mut self: Box<Self>,
        keys: &Keys,
    ) -> sop::Result<Box<dyn sop::ops::Decrypt<'a, RPGSOPOCT, Certs, Keys> + 'a>> {
        keys.keys
            .iter()
            .for_each(|tsk| self.decryption_keys.push(tsk.clone()));
        Ok(self)
    }

    fn with_key_password(
        mut self: Box<Self>,
        password: sop::Password,
    ) -> sop::Result<Box<dyn sop::ops::Decrypt<'a, RPGSOPOCT, Certs, Keys> + 'a>> {
        self.key_passwords.push(password);
        Ok(self)
    }

    fn ciphertext<'d>(
        self: Box<Self>,
        ciphertext: &'d mut (dyn io::Read + Send + Sync),
    ) -> sop::Result<
        Box<dyn sop::ops::Ready<(Option<sop::SessionKey>, Vec<sop::ops::Verification>)> + 'd>,
    >
    where
        'a: 'd,
    {
        Ok(Box::new(DecryptReady {
            decrypt: *self,
            ciphertext,
        }))
    }
}

struct DecryptReady<'a> {
    decrypt: Decrypt,
    ciphertext: &'a mut (dyn io::Read + Send + Sync),
}

impl sop::ops::Ready<(Option<sop::SessionKey>, Vec<sop::ops::Verification>)> for DecryptReady<'_> {
    fn to_writer(
        self: Box<Self>,
        sink: &mut (dyn io::Write + Send + Sync),
    ) -> sop::Result<(Option<sop::SessionKey>, Vec<sop::ops::Verification>)> {
        let (msg, _) =
            Message::from_reader(DebugWrapper(BufReader::new(self.ciphertext))).expect("FIXME");

        let Message::Encrypted { esk, .. } = &msg else {
            panic!("not encrypted");
        };

        let mut sk = None;
        for cert in &self.decrypt.decryption_keys {
            for dec in cert.decryption_capable_component_keys() {
                let mut card = get_card(
                    dec.public_params(),
                    openpgp_card::ocard::KeyType::Decryption,
                )
                .expect("FIXME");

                for e in esk {
                    if let Esk::PublicKeyEncryptedSessionKey(pkesk) = e {
                        let res = card::do_on_card(
                            &mut card,
                            openpgp_card::ocard::KeyType::Decryption,
                            &self.decrypt.key_passwords,
                            &|| {},
                            |cs| {
                                let sk = if let Ok((session_key, session_key_algorithm)) =
                                    cs.decrypt(pkesk.values().expect("FIXME"))
                                {
                                    Some(PlainSessionKey::V3_4 {
                                        key: session_key,
                                        sym_alg: session_key_algorithm,
                                    })
                                } else {
                                    None
                                };

                                Ok(sk)
                            },
                        );

                        if let Ok(Some(psk)) = res {
                            sk = Some(psk);
                            break;
                        }
                    }
                }
            }
        }

        let Some(sk) = sk else {
            unimplemented!("no session key obtained")
        };

        let inner = msg.decrypt_with_session_key(sk.clone()).unwrap();

        // FIXME: use rpgpie unwrap? + set session key afterwards?

        let Ok(mr) =
            rpgpie::message::unpack(inner, vec![], vec![], vec![], &[], &self.decrypt.verify)
        else {
            return Err(Error::BadData);
        };

        let session_key = match sk {
            PlainSessionKey::V3_4 { sym_alg, ref key } => {
                sop::SessionKey::new(u8::from(sym_alg), key).ok()
            }
            _ => None,
        };

        let verifications = util::result_to_verifications(&mr);

        sink.write_all(&mr.cleartext).expect("FIXME");

        Ok((session_key, verifications))
    }
}