sequoia-git 0.5.0

A tool for managing and enforcing a commit signing policy.
Documentation
use git2::Oid;

use serde::ser::Error;

use sequoia_openpgp::{
    Cert,
    Fingerprint,
    Packet,
    armor::{
        Kind,
        Writer,
    },
    cert::{
        prelude::{SubordinateKeyAmalgamation, UserIDAmalgamation},
    },
    packet::{
        key::PublicParts,
        Signature,
    },
    serialize::Serialize,
};

use crate::{
    Result,
};

pub fn prune_cert<S, U>(c: Cert,
                        subkeys: S, userids: U)
                        -> Result<Cert>
where
    S: FnMut(&SubordinateKeyAmalgamation<PublicParts>) -> bool,
    U: FnMut(&UserIDAmalgamation) -> bool,
{
    // Remove all user attributes, keep only the subkeys that
    // are plausible signing subkeys.
    let c = c.retain_user_attributes(|_| false)
        .retain_subkeys(|s| s.self_signatures().any(
            |s| s.key_flags().map(|f| f.for_signing())
                .unwrap_or(false)));

    // Filter out any third-party certifications.
    let mut acc = Vec::new();

    // The primary key and related signatures.
    let pk_bundle = c.primary_key().bundle();
    acc.push(pk_bundle.key().clone().into());
    for s in pk_bundle.self_signatures() {
        acc.push(s.clone().into())
    }
    for s in pk_bundle.self_revocations() {
        acc.push(s.clone().into())
    }

    // The subkeys and related signatures.
    for skb in c.keys().subkeys().filter(subkeys) {
        acc.push(skb.key().clone().into());
        for s in skb.self_signatures() {
            acc.push(s.clone().into())
        }
        for s in skb.self_revocations() {
            acc.push(s.clone().into())
        }
    }

    // The UserIDs.
    for uidb in c.userids().filter(userids) {
        acc.push(uidb.userid().clone().into());
        for s in uidb.self_signatures() {
            acc.push(s.clone().into())
        }
        for s in uidb.self_revocations() {
            acc.push(s.clone().into())
        }
    }

    Ok(Cert::from_packets(acc.into_iter())?)
}

#[allow(dead_code)]
pub fn deserialize_oid<'de, D>(deserializer: D)
                               -> std::result::Result<Oid, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::de::{Deserialize, Error};

    String::deserialize(deserializer)
        .and_then(|s| s.parse().map_err(|e| Error::custom(e)))
}

pub fn serialize_oid<S>(v: &Oid, serializer: S)
                        -> std::result::Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serializer.serialize_str(&v.to_string())
}

#[allow(dead_code)]
pub fn serialize_optional_oid<S>(v: &Option<&Oid>, serializer: S)
                                 -> std::result::Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    if let Some(v) = v {
        serializer.serialize_str(&v.to_string())
    } else {
        serializer.serialize_none()
    }
}

pub fn serialize_fp<S>(v: &Fingerprint, serializer: S)
                       -> std::result::Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serializer.serialize_str(&format!("{}", v))
}

pub fn serialize_packet<S>(p: &Packet, serializer: S)
                       -> std::result::Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    let mut writer = Writer::new(Vec::new(), Kind::File).map_err(Error::custom)?;
    p.serialize(&mut writer).map_err(Error::custom)?;
    let buffer = writer.finalize().map_err(Error::custom)?;

    serializer.serialize_str(&String::from_utf8_lossy(&buffer))
}

pub fn serialize_signature<S>(s: &Signature, serializer: S)
                       -> std::result::Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serialize_packet(&Packet::from(s.clone()), serializer)
}