pgp 0.10.2

OpenPGP implementation in Rust
Documentation
use chrono::{self, SubsecRound};
use smallvec::SmallVec;

use crate::composed::SignedKeyDetails;
use crate::crypto::hash::HashAlgorithm;
use crate::crypto::sym::SymmetricKeyAlgorithm;
use crate::errors::Result;
use crate::packet::{
    KeyFlags, PacketTrait, SignatureConfigBuilder, SignatureType, Subpacket, SubpacketData,
    UserAttribute, UserId,
};
use crate::types::{CompressionAlgorithm, RevocationKey, SecretKeyTrait};

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct KeyDetails {
    primary_user_id: UserId,
    user_ids: Vec<UserId>,
    user_attributes: Vec<UserAttribute>,
    keyflags: KeyFlags,
    preferred_symmetric_algorithms: SmallVec<[SymmetricKeyAlgorithm; 8]>,
    preferred_hash_algorithms: SmallVec<[HashAlgorithm; 8]>,
    preferred_compression_algorithms: SmallVec<[CompressionAlgorithm; 8]>,
    revocation_key: Option<RevocationKey>,
}

impl KeyDetails {
    #[allow(clippy::too_many_arguments)] // FIXME
    pub fn new(
        primary_user_id: UserId,
        user_ids: Vec<UserId>,
        user_attributes: Vec<UserAttribute>,
        keyflags: KeyFlags,
        preferred_symmetric_algorithms: SmallVec<[SymmetricKeyAlgorithm; 8]>,
        preferred_hash_algorithms: SmallVec<[HashAlgorithm; 8]>,
        preferred_compression_algorithms: SmallVec<[CompressionAlgorithm; 8]>,
        revocation_key: Option<RevocationKey>,
    ) -> Self {
        KeyDetails {
            primary_user_id,
            user_ids,
            user_attributes,
            keyflags,
            preferred_symmetric_algorithms,
            preferred_hash_algorithms,
            preferred_compression_algorithms,
            revocation_key,
        }
    }

    pub fn sign<F>(self, key: &impl SecretKeyTrait, key_pw: F) -> Result<SignedKeyDetails>
    where
        F: (FnOnce() -> String) + Clone,
    {
        let keyflags: SmallVec<[u8; 1]> = self.keyflags.into();
        let preferred_symmetric_algorithms = self.preferred_symmetric_algorithms;
        let preferred_hash_algorithms = self.preferred_hash_algorithms;
        let preferred_compression_algorithms = self.preferred_compression_algorithms;
        let revocation_key = self.revocation_key;

        let mut users = vec![];

        // primary user id
        {
            let id = self.primary_user_id;
            let mut hashed_subpackets = vec![
                Subpacket::regular(SubpacketData::IsPrimary(true)),
                Subpacket::regular(SubpacketData::SignatureCreationTime(
                    chrono::Utc::now().trunc_subsecs(0),
                )),
                Subpacket::regular(SubpacketData::KeyFlags(keyflags.clone())),
                Subpacket::regular(SubpacketData::PreferredSymmetricAlgorithms(
                    preferred_symmetric_algorithms.clone(),
                )),
                Subpacket::regular(SubpacketData::PreferredHashAlgorithms(
                    preferred_hash_algorithms.clone(),
                )),
                Subpacket::regular(SubpacketData::PreferredCompressionAlgorithms(
                    preferred_compression_algorithms.clone(),
                )),
                Subpacket::regular(SubpacketData::IssuerFingerprint(
                    Default::default(),
                    SmallVec::from_slice(&key.fingerprint()),
                )),
            ];
            if let Some(rkey) = revocation_key {
                hashed_subpackets.push(Subpacket::regular(SubpacketData::RevocationKey(rkey)));
            }

            let config = SignatureConfigBuilder::default()
                .typ(SignatureType::CertGeneric)
                .pub_alg(key.algorithm())
                .hashed_subpackets(hashed_subpackets)
                .unhashed_subpackets(vec![Subpacket::regular(SubpacketData::Issuer(
                    key.key_id(),
                ))])
                .build()?;

            let sig = config.sign_certificate(key, key_pw.clone(), id.tag(), &id)?;

            users.push(id.into_signed(sig));
        }

        // other user ids

        users.extend(
            self.user_ids
                .into_iter()
                .map(|id| {
                    let config = SignatureConfigBuilder::default()
                        .typ(SignatureType::CertGeneric)
                        .pub_alg(key.algorithm())
                        .hashed_subpackets(vec![
                            Subpacket::regular(SubpacketData::SignatureCreationTime(
                                chrono::Utc::now().trunc_subsecs(0),
                            )),
                            Subpacket::regular(SubpacketData::KeyFlags(keyflags.clone())),
                            Subpacket::regular(SubpacketData::PreferredSymmetricAlgorithms(
                                preferred_symmetric_algorithms.clone(),
                            )),
                            Subpacket::regular(SubpacketData::PreferredHashAlgorithms(
                                preferred_hash_algorithms.clone(),
                            )),
                            Subpacket::regular(SubpacketData::PreferredCompressionAlgorithms(
                                preferred_compression_algorithms.clone(),
                            )),
                            Subpacket::regular(SubpacketData::IssuerFingerprint(
                                Default::default(),
                                SmallVec::from_slice(&key.fingerprint()),
                            )),
                        ])
                        .unhashed_subpackets(vec![Subpacket::regular(SubpacketData::Issuer(
                            key.key_id(),
                        ))])
                        .build()?;

                    let sig = config.sign_certificate(key, key_pw.clone(), id.tag(), &id)?;

                    Ok(id.into_signed(sig))
                })
                .collect::<Result<Vec<_>>>()?,
        );

        let user_attributes = self
            .user_attributes
            .into_iter()
            .map(|u| u.sign(key, key_pw.clone()))
            .collect::<Result<Vec<_>>>()?;

        Ok(SignedKeyDetails {
            revocation_signatures: Default::default(),
            direct_signatures: Default::default(),
            users,
            user_attributes,
        })
    }
}