use super::{Logger, Validator, X509NameStructureValidator};
use crate::certificate::*;
use crate::extensions::{GeneralName, ParsedExtension};
use crate::public_key::PublicKey;
use crate::x509::{SubjectPublicKeyInfo, X509Version};
#[derive(Debug, Default)]
pub struct X509StructureValidator;
impl<'a> Validator<'a> for X509StructureValidator {
type Item = X509Certificate<'a>;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
res &= TbsCertificateStructureValidator.validate(&item.tbs_certificate, l);
res
}
}
#[derive(Debug, Default)]
pub struct TbsCertificateStructureValidator;
impl<'a> Validator<'a> for TbsCertificateStructureValidator {
type Item = TbsCertificate<'a>;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
if item.version.0 >= 3 {
l.err("Invalid version");
res = false;
}
let b = item.raw_serial();
if b.is_empty() {
l.err("Serial is empty");
res = false;
} else {
if b[0] & 0x80 != 0 {
l.warn("Serial number is negative");
}
if b.len() > 1 && b[0] == 0 && b[1] & 0x80 == 0 {
l.warn("Leading zeroes in serial number");
}
}
res &= X509NameStructureValidator.validate(&item.subject, l);
res &= X509NameStructureValidator.validate(&item.issuer, l);
res &= X509PublicKeyValidator.validate(&item.subject_pki, l);
let validity = item.validity();
let year_notbefore = validity.not_before.to_datetime().year();
if year_notbefore <= 2049 {
if !validity.not_before.is_utctime() {
l.warn("year <= 2049 should use UTCTime (notBefore)");
}
} else if !validity.not_before.is_generalizedtime() {
l.warn("year >= 2050 should use GeneralizedTime (notBefore)");
}
let year_notafter = validity.not_after.to_datetime().year();
if year_notafter <= 2049 {
if !validity.not_after.is_utctime() {
l.warn("year <= 2049 should use UTCTime (notAfter)");
}
} else if !validity.not_after.is_generalizedtime() {
l.warn("year >= 2050 should use GeneralizedTime (notAfter)");
}
if item.version == X509Version::V1 {
if item.issuer_uid.is_some() {
l.warn("issuerUniqueID present but version 1");
}
if item.subject_uid.is_some() {
l.warn("subjectUniqueID present but version 1");
}
}
if !item.extensions().is_empty() && item.version != X509Version::V3 {
l.err("Extensions present but version is not 3");
res = false;
}
for ext in item.extensions() {
if let ParsedExtension::UnsupportedExtension { .. } = &ext.parsed_extension {
l.warn(&format!("Unsupported extension {}", ext.oid));
}
if let ParsedExtension::ParseError { error } = &ext.parsed_extension {
l.err(&format!("Parse error in extension {}: {}", ext.oid, error));
res = false;
}
}
for ext in item.extensions() {
if let ParsedExtension::SubjectAlternativeName(san) = ext.parsed_extension() {
for name in &san.general_names {
match name {
GeneralName::DNSName(ref s) | GeneralName::RFC822Name(ref s) => {
if !s.as_bytes().iter().all(u8::is_ascii) {
l.warn(&format!("Invalid charset in 'SAN' entry '{s}'"));
}
}
_ => (),
}
}
}
}
res
}
}
#[derive(Debug, Default)]
pub struct X509PublicKeyValidator;
impl<'a> Validator<'a> for X509PublicKeyValidator {
type Item = SubjectPublicKeyInfo<'a>;
fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
let mut res = true;
match item.parsed() {
Ok(PublicKey::RSA(rsa)) => {
if rsa.modulus[0] & 0x80 != 0 {
l.warn("Public key: (RSA) modulus is negative");
}
if rsa.exponent[0] & 0x80 != 0 {
l.warn("Public key: (RSA) exponent is negative");
}
}
Ok(PublicKey::Unknown(_b)) => {
l.warn("Unknown public key type");
}
Ok(_) => {}
Err(_) => {
l.err("Invalid public key");
res = false;
}
}
res
}
}