wecanencrypt 0.7.1

Simple Rust OpenPGP library for encryption, signing, and key management.
Documentation
//! Keyring file operations.
//!
//! This module provides functions for reading and writing OpenPGP
//! keyring files that contain multiple certificates.

use std::io::Cursor;
use std::path::Path;

use pgp::composed::{Deserializable, SignedPublicKey};
use pgp::ser::Serialize;

use crate::error::{Error, Result};
use crate::internal::{fingerprint_to_hex, parse_cert, public_key_to_armored};
use crate::parse::parse_cert_bytes;
use crate::types::CertificateInfo;

/// Parse a keyring file containing multiple certificates.
///
/// # Arguments
/// * `path` - Path to the keyring file
///
/// # Returns
/// A list of (CertificateInfo, raw_bytes) for each certificate in the keyring.
///
/// # Example
/// ```ignore
/// // Ignored: illustrative example with placeholder file path
/// let certs = parse_keyring_file("pubring.gpg")?;
/// for (info, bytes) in certs {
///     println!("Key: {} - {}", info.fingerprint, info.user_ids.first().unwrap_or(&"".to_string()));
/// }
/// ```
pub fn parse_keyring_file(path: impl AsRef<Path>) -> Result<Vec<(CertificateInfo, Vec<u8>)>> {
    let keyring_data = std::fs::read(path.as_ref())?;
    parse_keyring_bytes(&keyring_data)
}

/// Parse keyring data containing multiple certificates.
///
/// # Arguments
/// * `data` - Keyring data (armored or binary)
///
/// # Returns
/// A list of (CertificateInfo, raw_bytes) for each certificate.
pub fn parse_keyring_bytes(data: &[u8]) -> Result<Vec<(CertificateInfo, Vec<u8>)>> {
    let mut results = Vec::new();

    // Try to parse as multiple public keys
    let cursor = Cursor::new(data);
    let (keys_iter, _headers) =
        SignedPublicKey::from_reader_many(cursor).map_err(|e| Error::Parse(e.to_string()))?;

    for key_result in keys_iter {
        match key_result {
            Ok(key) => {
                let bytes = key.to_bytes().map_err(|e| Error::Crypto(e.to_string()))?;
                let info = parse_cert_bytes(&bytes, true)?;
                results.push((info, bytes));
            }
            Err(e) => {
                // Log error but continue parsing other certs
                eprintln!("Warning: failed to parse certificate: {}", e);
            }
        }
    }

    Ok(results)
}

/// Export multiple certificates to a keyring file.
///
/// # Arguments
/// * `certs` - Slice of certificate data
/// * `output` - Path to write the keyring file
///
/// # Example
/// ```ignore
/// // Ignored: illustrative example with placeholder file paths
/// let cert1 = std::fs::read("key1.asc")?;
/// let cert2 = std::fs::read("key2.asc")?;
/// export_keyring_file(&[&cert1, &cert2], "combined.gpg")?;
/// ```
pub fn export_keyring_file(certs: &[&[u8]], output: impl AsRef<Path>) -> Result<()> {
    let mut keyring_data = Vec::new();

    for cert_data in certs {
        let (public_key, _is_secret) = parse_cert(cert_data)?;
        let bytes = public_key
            .to_bytes()
            .map_err(|e| Error::Crypto(e.to_string()))?;
        keyring_data.extend_from_slice(&bytes);
    }

    std::fs::write(output.as_ref(), keyring_data)?;
    Ok(())
}

/// Export multiple certificates to an armored keyring.
///
/// # Arguments
/// * `certs` - Slice of certificate data
///
/// # Returns
/// ASCII-armored keyring containing all certificates.
pub fn export_keyring_armored(certs: &[&[u8]]) -> Result<String> {
    let mut all_armored = String::new();

    for cert_data in certs {
        let (public_key, _is_secret) = parse_cert(cert_data)?;
        let armored = public_key_to_armored(&public_key)?;
        all_armored.push_str(&armored);
        all_armored.push('\n');
    }

    Ok(all_armored)
}

/// Merge two certificates (e.g., adding new signatures).
///
/// Note: This is a simplified implementation. Full merging would
/// require complex signature handling.
///
/// # Arguments
/// * `cert_data` - The original certificate
/// * `new_cert_data` - The certificate with new data to merge
/// * `force` - If true, merge even if the keys have different fingerprints
///
/// # Returns
/// The merged certificate.
pub fn merge_keys(cert_data: &[u8], new_cert_data: &[u8], force: bool) -> Result<Vec<u8>> {
    let (cert1, _) = parse_cert(cert_data)?;
    let (cert2, _) = parse_cert(new_cert_data)?;

    let fp1 = fingerprint_to_hex(&cert1.primary_key);
    let fp2 = fingerprint_to_hex(&cert2.primary_key);

    if fp1 != fp2 && !force {
        return Err(Error::InvalidInput(format!(
            "Certificate fingerprints do not match: {} vs {}",
            fp1, fp2
        )));
    }

    // For now, just check if they're the same and return an error
    let bytes1 = cert1.to_bytes().map_err(|e| Error::Crypto(e.to_string()))?;
    let bytes2 = cert2.to_bytes().map_err(|e| Error::Crypto(e.to_string()))?;

    if bytes1 == bytes2 {
        return Err(Error::SameKeyError);
    }

    // TODO: Implement proper merging. For now, return the newer certificate
    // A proper implementation would merge signatures, UIDs, etc.
    Ok(bytes2)
}

#[cfg(test)]
mod tests {
    // Tests would require key fixtures
}