rsop-oct 0.1.4

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

mod card;
mod cmd;
mod util;

use std::io;

use pgp::packet::Signature;
use rpgpie::certificate::Certificate;

/// Error type for this crate
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Error {
    #[error("rPGP error: {0}")]
    Rpgp(pgp::errors::Error),

    #[error("rpgpie error: {0}")]
    Rpgpie(rpgpie::Error),

    #[error("OcardRpgp error: {0}")]
    OcardRpgp(openpgp_card_rpgp::Error),

    #[error("Internal error: {0}")]
    Message(String),
}

impl From<pgp::errors::Error> for Error {
    fn from(value: pgp::errors::Error) -> Self {
        Error::Rpgp(value)
    }
}

impl From<rpgpie::Error> for Error {
    fn from(value: rpgpie::Error) -> Self {
        Error::Rpgpie(value)
    }
}

impl From<openpgp_card_rpgp::Error> for Error {
    fn from(value: openpgp_card_rpgp::Error) -> Self {
        Error::OcardRpgp(value)
    }
}

#[derive(Clone, Copy, Default)]
pub struct RPGSOPOCT {}

// SOP singleton
const SOP: RPGSOPOCT = RPGSOPOCT {};

pub struct Certs {
    certs: Vec<Certificate>,
    source_name: Option<String>,
}

pub struct Keys {
    keys: Vec<Certificate>, // certificates that are backed by keys on OpenPGP cards
    source_name: Option<String>,
}

pub struct Sigs {
    sigs: Vec<Signature>,
    source_name: Option<String>,
}

impl sop::SOP<'_> for RPGSOPOCT {
    type Keys = Keys;
    type Certs = Certs;
    type Sigs = Sigs;

    fn version(&'_ self) -> sop::Result<Box<dyn sop::ops::Version<'_>>> {
        Ok(Box::new(cmd::version::Version::new()))
    }

    fn generate_key(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::GenerateKey<'_, Self, Self::Keys> + '_>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn change_key_password(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::ChangeKeyPassword<'_, Self, Self::Keys>>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn revoke_key(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::RevokeKey<'_, Self, Self::Certs, Self::Keys>>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn extract_cert(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::ExtractCert<'_, Self, Self::Certs, Self::Keys> + '_>> {
        Err(sop::errors::Error::NotImplemented)
    }

    // Operations that don't involve private key material, based on rpgpie-sop

    fn update_key(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::UpdateKey<'_, Self, Self::Certs, Self::Keys> + '_>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn merge_certs(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::MergeCerts<'_, Self, Self::Certs> + '_>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn certify_userid(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::CertifyUserID<'_, Self, Self::Certs, Self::Keys> + '_>> {
        Ok(Box::new(cmd::certify::CertifyUserID::new()))
    }

    fn validate_userid(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::ValidateUserID<'_, Self, Self::Certs> + '_>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn sign(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::Sign<'_, Self, Self::Keys, Self::Sigs> + '_>> {
        Ok(Box::new(cmd::sign::Sign::new()))
    }

    fn verify(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::Verify<'_, Self, Self::Certs, Self::Sigs> + '_>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn encrypt(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::Encrypt<'_, Self, Self::Certs, Self::Keys> + '_>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn decrypt(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::Decrypt<'_, Self, Self::Certs, Self::Keys> + '_>> {
        Ok(Box::new(cmd::decrypt::Decrypt::new()))
    }

    fn armor(&'_ self) -> sop::Result<Box<dyn sop::ops::Armor<'_>>> {
        Err(sop::errors::Error::NotImplemented)
    }

    fn dearmor(&'_ self) -> sop::Result<Box<dyn sop::ops::Dearmor<'_>>> {
        Err(sop::errors::Error::NotImplemented)
    }
    fn inline_detach(&'_ self) -> sop::Result<Box<dyn sop::ops::InlineDetach<'_, Self::Sigs>>> {
        Err(sop::errors::Error::NotImplemented)
    }
    fn inline_verify(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::InlineVerify<'_, Self, Self::Certs> + '_>> {
        Err(sop::errors::Error::NotImplemented)
    }
    fn inline_sign(
        &'_ self,
    ) -> sop::Result<Box<dyn sop::ops::InlineSign<'_, Self, Self::Keys> + '_>> {
        Ok(Box::new(cmd::inline_sign::InlineSign::new()))
    }
}

impl sop::Load<'_, RPGSOPOCT> for Certs {
    fn from_reader(
        _sop: &RPGSOPOCT,
        mut source: &mut (dyn io::Read + Send + Sync),
        source_name: Option<String>,
    ) -> sop::Result<Self> {
        let certs = Certificate::load(&mut source).expect("FIXME");

        Ok(Certs { certs, source_name })
    }

    fn source_name(&self) -> Option<&str> {
        self.source_name.as_deref()
    }
}

impl sop::Save for Certs {
    fn to_writer(
        &self,
        armored: bool,
        sink: &mut (dyn io::Write + Send + Sync),
    ) -> sop::Result<()> {
        Certificate::save_all(&self.certs, armored, sink).expect("FIXME");

        Ok(())
    }
}

impl sop::Load<'_, RPGSOPOCT> for Keys {
    fn from_reader(
        _sop: &'_ RPGSOPOCT,
        mut source: &mut (dyn io::Read + Send + Sync),
        source_name: Option<String>,
    ) -> sop::Result<Self> {
        let keys = Certificate::load(&mut source).expect("FIXME");

        Ok(Keys { keys, source_name })
    }

    fn source_name(&self) -> Option<&str> {
        self.source_name.as_deref()
    }
}

impl sop::Save for Keys {
    fn to_writer(
        &self,
        _armored: bool,
        _sink: &mut (dyn io::Write + Send + Sync),
    ) -> sop::Result<()> {
        Err(sop::errors::Error::NotImplemented)
    }
}

impl sop::Load<'_, RPGSOPOCT> for Sigs {
    fn from_reader(
        _sop: &'_ RPGSOPOCT,
        mut source: &mut (dyn io::Read + Send + Sync),
        source_name: Option<String>,
    ) -> sop::Result<Self> {
        let sigs = rpgpie::signature::load(&mut source).expect("FIXME");

        Ok(Sigs { sigs, source_name })
    }

    fn source_name(&self) -> Option<&str> {
        self.source_name.as_deref()
    }
}

impl sop::Save for Sigs {
    fn to_writer(
        &self,
        armored: bool,
        mut sink: &mut (dyn io::Write + Send + Sync),
    ) -> sop::Result<()> {
        rpgpie::signature::save(&self.sigs, armored, &mut sink).expect("FIXME");

        Ok(())
    }
}

impl<'s> sop::plumbing::SopRef<'s, RPGSOPOCT> for Certs {
    fn sop(&self) -> &'s RPGSOPOCT {
        &SOP
    }
}

impl<'s> sop::plumbing::SopRef<'s, RPGSOPOCT> for Keys {
    fn sop(&self) -> &'s RPGSOPOCT {
        &SOP
    }
}

impl<'s> sop::plumbing::SopRef<'s, RPGSOPOCT> for Sigs {
    fn sop(&self) -> &'s RPGSOPOCT {
        &SOP
    }
}