use std::path::Path;
use crate::pgp::composed::SignedPublicKey;
use crate::pgp::types::KeyDetails;
use crate::error::Result;
use crate::internal::{
fingerprint_to_hex, get_algorithm_name, get_key_bit_size, is_subkey_revoked, is_subkey_valid,
keyid_to_hex, parse_cert, system_time_to_datetime,
};
use crate::types::{AvailableSubkey, CertificateInfo, KeyCipherDetails, KeyType, SubkeyInfo};
pub fn parse_cert_bytes(data: &[u8], allow_expired: bool) -> Result<CertificateInfo> {
let (public_key, is_secret) = parse_cert(data)?;
extract_cert_info(&public_key, is_secret, allow_expired)
}
pub fn parse_cert_file(path: impl AsRef<Path>, allow_expired: bool) -> Result<CertificateInfo> {
let data = std::fs::read(path.as_ref())?;
parse_cert_bytes(&data, allow_expired)
}
pub fn get_key_cipher_details(data: &[u8]) -> Result<Vec<KeyCipherDetails>> {
let (public_key, _) = parse_cert(data)?;
let mut details = Vec::new();
details.push(KeyCipherDetails {
fingerprint: fingerprint_to_hex(&public_key.primary_key),
algorithm: get_algorithm_name(&public_key.primary_key),
bit_length: get_key_bit_size(&public_key.primary_key),
});
for subkey in &public_key.public_subkeys {
details.push(KeyCipherDetails {
fingerprint: fingerprint_to_hex(&subkey.key),
algorithm: get_algorithm_name(&subkey.key),
bit_length: get_key_bit_size(&subkey.key),
});
}
Ok(details)
}
fn extract_cert_info(
public_key: &SignedPublicKey,
is_secret: bool,
allow_expired: bool,
) -> Result<CertificateInfo> {
let user_ids: Vec<String> = public_key
.details
.users
.iter()
.map(|u| String::from_utf8_lossy(u.id.id()).to_string())
.collect();
let fingerprint = fingerprint_to_hex(&public_key.primary_key);
let key_id = keyid_to_hex(&public_key.primary_key);
let creation_time = system_time_to_datetime(public_key.primary_key.created_at().into());
let expiration_time = crate::internal::get_key_expiration(public_key).map(system_time_to_datetime);
let can_primary_sign = crate::internal::can_primary_sign(public_key);
let subkeys = extract_subkey_info(public_key, allow_expired);
Ok(CertificateInfo {
user_ids,
fingerprint,
key_id,
is_secret,
creation_time,
expiration_time,
can_primary_sign,
subkeys,
})
}
fn extract_subkey_info(public_key: &SignedPublicKey, allow_expired: bool) -> Vec<SubkeyInfo> {
let mut subkeys = Vec::new();
for subkey in &public_key.public_subkeys {
let key_id = keyid_to_hex(&subkey.key);
let fingerprint = fingerprint_to_hex(&subkey.key);
let creation_time = system_time_to_datetime(subkey.key.created_at().into());
let expiration_time = subkey.signatures.first().and_then(|sig| {
sig.key_expiration_time().map(|validity| {
let creation: std::time::SystemTime = subkey.key.created_at().into();
system_time_to_datetime(creation + validity.into())
})
});
let is_revoked = is_subkey_revoked(subkey);
let algorithm = get_algorithm_name(&subkey.key);
let bit_length = get_key_bit_size(&subkey.key);
let key_type = determine_key_type(subkey);
if allow_expired || is_subkey_valid(subkey, false) {
subkeys.push(SubkeyInfo {
key_id,
fingerprint,
creation_time,
expiration_time,
key_type,
is_revoked,
algorithm,
bit_length,
});
}
}
subkeys
}
fn determine_key_type(subkey: &crate::pgp::composed::SignedPublicSubKey) -> KeyType {
for sig in &subkey.signatures {
let flags = sig.key_flags();
if flags.encrypt_comms() || flags.encrypt_storage() {
return KeyType::Encryption;
} else if flags.sign() {
return KeyType::Signing;
} else if flags.authentication() {
return KeyType::Authentication;
} else if flags.certify() {
return KeyType::Certification;
}
}
KeyType::Unknown
}
pub fn get_available_encryption_subkeys(data: &[u8]) -> Result<Vec<AvailableSubkey>> {
get_available_subkeys_by_type(data, |flags| {
flags.encrypt_comms() || flags.encrypt_storage()
})
}
pub fn get_available_signing_subkeys(data: &[u8]) -> Result<Vec<AvailableSubkey>> {
get_available_subkeys_by_type(data, |flags| flags.sign())
}
pub fn get_available_authentication_subkeys(data: &[u8]) -> Result<Vec<AvailableSubkey>> {
get_available_subkeys_by_type(data, |flags| flags.authentication())
}
pub fn get_all_available_subkeys(data: &[u8]) -> Result<Vec<AvailableSubkey>> {
get_available_subkeys_by_type(data, |_| true)
}
fn get_available_subkeys_by_type<F>(data: &[u8], predicate: F) -> Result<Vec<AvailableSubkey>>
where
F: Fn(&crate::pgp::packet::KeyFlags) -> bool,
{
let (public_key, _) = parse_cert(data)?;
let mut available = Vec::new();
for subkey in &public_key.public_subkeys {
if is_subkey_revoked(subkey) {
continue;
}
if !is_subkey_valid(subkey, false) {
continue;
}
let matches_predicate = subkey.signatures.iter().any(|sig| {
let flags = sig.key_flags();
predicate(&flags)
});
if !matches_predicate {
continue;
}
let key_type = determine_key_type(subkey);
let expiration_time = subkey.signatures.first().and_then(|sig| {
sig.key_expiration_time().map(|validity| {
let creation: std::time::SystemTime = subkey.key.created_at().into();
system_time_to_datetime(creation + validity.into())
})
});
available.push(AvailableSubkey {
fingerprint: fingerprint_to_hex(&subkey.key),
key_id: keyid_to_hex(&subkey.key),
creation_time: system_time_to_datetime(subkey.key.created_at().into()),
expiration_time,
key_type,
algorithm: get_algorithm_name(&subkey.key),
bit_length: get_key_bit_size(&subkey.key),
});
}
Ok(available)
}
pub fn has_available_encryption_subkey(data: &[u8]) -> Result<bool> {
Ok(!get_available_encryption_subkeys(data)?.is_empty())
}
pub fn has_available_signing_subkey(data: &[u8]) -> Result<bool> {
Ok(!get_available_signing_subkeys(data)?.is_empty())
}
#[cfg(test)]
mod tests {
}