use std::io::Cursor;
use std::path::Path;
use crate::pgp::composed::{Deserializable, SignedPublicKey};
use crate::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;
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)
}
pub fn parse_keyring_bytes(data: &[u8]) -> Result<Vec<(CertificateInfo, Vec<u8>)>> {
let mut results = Vec::new();
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) => {
eprintln!("Warning: failed to parse certificate: {}", e);
}
}
}
Ok(results)
}
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(())
}
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)
}
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
)));
}
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);
}
Ok(bytes2)
}
#[cfg(test)]
mod tests {
}