use super::{
ComplianceLevel, ValidationResults, SIGNING_CREDENTIAL_OCSP_INACCESSIBLE,
SIGNING_CREDENTIAL_OCSP_SKIPPED, SIGNING_CREDENTIAL_OCSP_UNKNOWN, TIME_STAMP_TRUSTED,
};
pub const CONFORMANCE_OUT_OF_SCOPE: &str = "com.encypher.conformance.outOfScope";
pub const CONFORMANCE_SPEC_VERSION_NONCONFORMANT: &str =
"com.encypher.conformance.specVersionNonConformant";
pub const PROGRAM_REVOCATION_INFORMATION_MISSING: &str =
"com.encypher.conformance.revocationInformationMissing";
pub const PROGRAM_TRUSTED_TIMESTAMP_MISSING: &str =
"com.encypher.conformance.trustedTimeStampMissing";
pub const PROGRAM_FAILURE_CODES: &[&str] = &[
PROGRAM_REVOCATION_INFORMATION_MISSING,
PROGRAM_TRUSTED_TIMESTAMP_MISSING,
];
pub fn apply_compliance_upgrades(
results: &mut ValidationResults,
compliance: ComplianceLevel,
sig_url: &str,
) {
if compliance != ComplianceLevel::ConformanceProgram {
return;
}
if results.has_informational(SIGNING_CREDENTIAL_OCSP_SKIPPED)
|| results.has_informational(SIGNING_CREDENTIAL_OCSP_UNKNOWN)
|| results.has_informational(SIGNING_CREDENTIAL_OCSP_INACCESSIBLE)
{
results.push_failure(
PROGRAM_REVOCATION_INFORMATION_MISSING,
sig_url.to_string(),
"conformance program requires usable revocation information (SHALL); the core spec reports this as signingCredential.ocsp.skipped, .unknown, or .inaccessible".into(),
);
}
if !results.has_success(TIME_STAMP_TRUSTED) {
results.push_failure(
PROGRAM_TRUSTED_TIMESTAMP_MISSING,
sig_url.to_string(),
"conformance program requires a trusted time-stamp (SHALL)".into(),
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::c2pa_validate::{EngineProfile, SpecVersion};
fn with_skipped_ocsp() -> ValidationResults {
let mut results = ValidationResults::default();
results.push_informational(
SIGNING_CREDENTIAL_OCSP_SKIPPED,
"url".into(),
"no staple".into(),
);
results
}
fn codes(results: &ValidationResults) -> Vec<&str> {
results
.failure
.iter()
.map(|status| status.code.as_str())
.collect()
}
#[test]
fn core_spec_validation_adds_no_program_failures() {
let mut results = with_skipped_ocsp();
apply_compliance_upgrades(&mut results, ComplianceLevel::CoreSpec, "url");
assert!(results.failure.is_empty());
}
#[test]
fn program_outcomes_use_entity_namespaced_codes_only() {
let mut results = with_skipped_ocsp();
apply_compliance_upgrades(&mut results, ComplianceLevel::ConformanceProgram, "url");
assert_eq!(
codes(&results),
vec![
PROGRAM_REVOCATION_INFORMATION_MISSING,
PROGRAM_TRUSTED_TIMESTAMP_MISSING
]
);
assert!(!results.has_failure(SIGNING_CREDENTIAL_OCSP_SKIPPED));
assert!(results.has_informational(SIGNING_CREDENTIAL_OCSP_SKIPPED));
for status in &results.failure {
assert!(
status.code.starts_with("com.encypher."),
"program-only outcome {} must be entity-namespaced",
status.code
);
}
}
#[test]
fn a_trusted_timestamp_clears_the_timestamp_requirement() {
let mut results = ValidationResults::default();
results.push_success(TIME_STAMP_TRUSTED, "url".into(), "trusted".into());
apply_compliance_upgrades(&mut results, ComplianceLevel::ConformanceProgram, "url");
assert!(results.failure.is_empty());
}
#[test]
fn program_failures_still_invalidate_under_the_strict_verdict() {
let mut results = with_skipped_ocsp();
apply_compliance_upgrades(&mut results, ComplianceLevel::ConformanceProgram, "url");
assert_eq!(
super::super::compute_state(&results, EngineProfile::strict(SpecVersion::V2_4)),
super::super::ValidationState::Invalid
);
}
}