use crate::signature::Signature;
use sequoia_openpgp::cert::CertParser;
use sequoia_openpgp::parse::Parse;
use sequoia_openpgp::Cert;
use std::fmt;
use std::time::SystemTime;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KeyManagementError {
ParseError(String),
NoCertificates,
KeyExpired,
KeyNotValid,
KeyExpiringSoon(u64),
FingerprintMismatch {
expected: String,
actual: String,
},
InvalidFingerprint(String),
InvalidFingerprintLength {
length: usize,
expected: String,
},
}
impl fmt::Display for KeyManagementError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
KeyManagementError::ParseError(msg) => write!(f, "Failed to parse key data: {}", msg),
KeyManagementError::NoCertificates => {
write!(f, "No valid certificates found in key data")
}
KeyManagementError::KeyExpired => write!(f, "Key has expired"),
KeyManagementError::KeyNotValid => write!(f, "Key has expired or is not yet valid"),
KeyManagementError::KeyExpiringSoon(days) => write!(f, "Key expires in {} days", days),
KeyManagementError::FingerprintMismatch { expected, actual } => {
write!(
f,
"Fingerprint mismatch! Expected: {}, Got: {}",
expected, actual
)
}
KeyManagementError::InvalidFingerprint(msg) => write!(f, "{}", msg),
KeyManagementError::InvalidFingerprintLength { length, expected } => {
write!(
f,
"Invalid fingerprint length: {}. Expected {}",
length, expected
)
}
}
}
}
impl std::error::Error for KeyManagementError {}
pub fn check_key_expiration(cert: &Cert) -> Option<KeyManagementError> {
use sequoia_openpgp::policy::StandardPolicy;
let policy = StandardPolicy::new();
let now = SystemTime::now();
match cert.with_policy(&policy, now) {
Ok(valid_cert) => {
if let Some(expiration) = valid_cert.primary_key().key_expiration_time() {
if expiration <= now {
return Some(KeyManagementError::KeyExpired);
}
if let Ok(duration_until) = expiration.duration_since(now) {
let days_until = duration_until.as_secs() / 86400;
if days_until < 30 {
return Some(KeyManagementError::KeyExpiringSoon(days_until));
}
}
}
None
}
Err(_) => {
Some(KeyManagementError::KeyNotValid)
}
}
}
pub fn verify_key_fingerprint(
key_data: &str,
expected_fingerprint: &str,
) -> Result<(), KeyManagementError> {
let parser = CertParser::from_bytes(key_data.as_bytes())
.map_err(|e| KeyManagementError::ParseError(e.to_string()))?;
let certs: Result<Vec<Cert>, _> = parser.collect();
let certs = certs.map_err(|e| KeyManagementError::ParseError(e.to_string()))?;
if certs.is_empty() {
return Err(KeyManagementError::NoCertificates);
}
let cert = &certs[0];
let fingerprint = cert.fingerprint().to_hex();
let normalized_actual = fingerprint.replace(" ", "").to_uppercase();
let normalized_expected = expected_fingerprint.replace(" ", "").to_uppercase();
if normalized_actual != normalized_expected {
return Err(KeyManagementError::FingerprintMismatch {
expected: expected_fingerprint.to_string(),
actual: fingerprint,
});
}
Ok(())
}
pub fn create_inline_signature(key_data: &str) -> Result<Signature, KeyManagementError> {
let parser = CertParser::from_bytes(key_data.as_bytes())
.map_err(|e| KeyManagementError::ParseError(e.to_string()))?;
let certs: Result<Vec<Cert>, _> = parser.collect();
let certs = certs.map_err(|e| KeyManagementError::ParseError(e.to_string()))?;
if certs.is_empty() {
return Err(KeyManagementError::NoCertificates);
}
if let Some(error) = check_key_expiration(&certs[0]) {
eprintln!("Warning: {}", error);
}
Ok(Signature::KeyBlock(key_data.to_string()))
}
pub fn validate_fingerprint(fingerprint: &str) -> Result<String, KeyManagementError> {
let cleaned = fingerprint.replace(" ", "").to_uppercase();
if !cleaned.chars().all(|c| c.is_ascii_hexdigit()) {
return Err(KeyManagementError::InvalidFingerprint(
"Fingerprint must contain only hexadecimal characters".to_string(),
));
}
if cleaned.len() != 40 && cleaned.len() != 64 {
return Err(KeyManagementError::InvalidFingerprintLength {
length: cleaned.len(),
expected: "40 or 64 characters".to_string(),
});
}
Ok(cleaned)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_fingerprint() {
assert!(validate_fingerprint("1234567890ABCDEF1234567890ABCDEF12345678").is_ok());
assert!(validate_fingerprint("1234 5678 90AB CDEF 1234 5678 90AB CDEF 1234 5678").is_ok());
assert!(validate_fingerprint("XXXX567890ABCDEF1234567890ABCDEF12345678").is_err());
assert!(validate_fingerprint("1234567890ABCDEF").is_err());
}
}