#![allow(clippy::doc_lazy_continuation)]
use std::{collections::HashSet, error::Error, fmt, io::BufRead, str::FromStr};
use asn1_rs::{oid, Oid};
use async_generic::async_generic;
use thiserror::Error;
use x509_parser::{extensions::ExtendedKeyUsage, pem::Pem};
use crate::{
crypto::{base64, hash::sha256},
settings::TrustListKind,
};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum TrustAnchorType {
#[default]
Manifest,
TSA,
CAWG,
EndEntity,
NoCheck,
}
impl From<TrustListKind> for TrustAnchorType {
fn from(kind: TrustListKind) -> Self {
match kind {
TrustListKind::Manifest => TrustAnchorType::Manifest,
TrustListKind::TSA => TrustAnchorType::TSA,
TrustListKind::CAWG => TrustAnchorType::CAWG,
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TrustAnchor {
pub trust_anchor_ders: Vec<Vec<u8>>,
pub trust_anchor_uri: String,
pub trust_anchor_type: TrustAnchorType,
pub trust_config: Option<String>,
}
#[derive(Clone, Debug)]
pub struct CertificateTrustPolicy {
trust_anchors: Vec<TrustAnchor>,
end_entity_cert_set: HashSet<String>,
additional_ekus: HashSet<String>,
mandatory_ekus: HashSet<String>,
passthrough: bool,
}
impl Default for CertificateTrustPolicy {
fn default() -> Self {
let mut this = CertificateTrustPolicy {
trust_anchors: Vec::default(),
end_entity_cert_set: HashSet::default(),
additional_ekus: HashSet::default(),
mandatory_ekus: HashSet::default(),
passthrough: false,
};
this.add_valid_ekus(include_bytes!("./valid_eku_oids.cfg"));
#[cfg(test)]
{
let _ = this.add_trust_anchors(
include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/test_cert_root_bundle.pem"
),
"https://c2pa-rs/unknown_tl",
TrustAnchorType::Manifest,
None,
);
}
this
}
}
impl CertificateTrustPolicy {
pub fn new() -> Self {
CertificateTrustPolicy {
trust_anchors: Vec::default(),
end_entity_cert_set: HashSet::default(),
additional_ekus: HashSet::default(),
mandatory_ekus: HashSet::default(),
passthrough: false,
}
}
pub fn passthrough() -> Self {
Self {
trust_anchors: Vec::default(),
end_entity_cert_set: HashSet::default(),
additional_ekus: HashSet::default(),
mandatory_ekus: HashSet::default(),
passthrough: true,
}
}
pub fn signing_trust_anchors(&self) -> Vec<&TrustAnchor> {
self.trust_anchors
.iter()
.filter(|a| a.trust_anchor_type == TrustAnchorType::Manifest)
.collect::<Vec<_>>()
}
pub fn tsa_trust_anchors(&self) -> Vec<&TrustAnchor> {
self.trust_anchors
.iter()
.filter(|a| a.trust_anchor_type == TrustAnchorType::TSA)
.collect::<Vec<_>>()
}
pub fn cawg_trust_anchors(&self) -> Vec<&TrustAnchor> {
self.trust_anchors
.iter()
.filter(|a| a.trust_anchor_type == TrustAnchorType::CAWG)
.collect::<Vec<_>>()
}
#[allow(unused)] #[async_generic]
pub fn check_certificate_trust(
&self,
chain_der: &[Vec<u8>],
end_entity_cert_der: &[u8],
signing_time_epoch: Option<i64>,
) -> Result<(TrustAnchorType, String), CertificateTrustError> {
if self.passthrough {
return Ok((TrustAnchorType::NoCheck, String::new()));
}
let cert_hash = base64_sha256_cert_der(end_entity_cert_der);
if self.end_entity_cert_set.contains(&cert_hash) {
return Ok((TrustAnchorType::EndEntity, String::new()));
}
#[cfg(any(feature = "rust_native_crypto", target_arch = "wasm32"))]
#[cfg(feature = "rust_native_crypto")]
{
return super::certificate_trust::rust_native::check_certificate_trust(
self,
chain_der,
end_entity_cert_der,
signing_time_epoch,
);
}
#[cfg(all(
feature = "openssl",
not(all(feature = "rust_native_crypto", target_arch = "wasm32"))
))]
{
return super::certificate_trust::openssl::check_certificate_trust(
self,
chain_der,
end_entity_cert_der,
signing_time_epoch,
);
}
Err(CertificateTrustError::InternalError(
"no implementation for certificate evaluation available".to_string(),
))
}
pub fn add_trust_anchors(
&mut self,
trust_anchor_pems: &[u8],
trust_list_uri: &str,
trust_anchor_type: TrustAnchorType,
trust_config: Option<String>,
) -> Result<(), InvalidCertificateError> {
let mut trust_anchor_ders = Vec::new();
let trust_anchor_pems = String::from_utf8_lossy(trust_anchor_pems)
.replace("\\n", "\n")
.into_bytes();
for maybe_pem in Pem::iter_from_buffer(&trust_anchor_pems) {
match maybe_pem {
Ok(pem) => trust_anchor_ders.push(pem.contents),
Err(e) => {
return Err(InvalidCertificateError(e.to_string()));
}
}
}
self.trust_anchors.push(TrustAnchor {
trust_anchor_ders,
trust_anchor_uri: trust_list_uri.to_string(),
trust_anchor_type,
trust_config,
});
Ok(())
}
pub fn add_end_entity_credentials(
&mut self,
end_entity_cert_pems: &[u8],
) -> Result<(), InvalidCertificateError> {
let mut inside_pem_block = false;
for line in end_entity_cert_pems.lines().map_while(Result::ok) {
if line.contains("-----BEGIN") {
inside_pem_block = true;
}
if line.contains("-----END") {
inside_pem_block = false;
}
if !inside_pem_block && line.len() == 44 && base64::decode(&line).is_ok() {
self.end_entity_cert_set.insert(line);
}
}
for maybe_pem in Pem::iter_from_buffer(end_entity_cert_pems) {
match maybe_pem {
Ok(pem) => {
self.end_entity_cert_set
.insert(base64_sha256_cert_der(&pem.contents));
}
Err(e) => {
return Err(InvalidCertificateError(e.to_string()));
}
}
}
Ok(())
}
pub fn add_default_valid_ekus(&mut self) {
self.add_valid_ekus(include_bytes!("./valid_eku_oids.cfg"));
}
pub fn add_valid_ekus(&mut self, eku_oids: &[u8]) {
let Ok(eku_oids) = std::str::from_utf8(eku_oids) else {
return;
};
for line in eku_oids.lines() {
if let Ok(_oid) = Oid::from_str(line) {
self.additional_ekus.insert(line.to_string());
}
}
}
pub(crate) fn has_mandatory_ekus(&self) -> bool {
!self.mandatory_ekus.is_empty()
}
pub fn add_mandatory_ekus(&mut self, eku_oids: &[u8]) {
let Ok(eku_oids) = std::str::from_utf8(eku_oids) else {
return;
};
for line in eku_oids.lines() {
if let Ok(_oid) = Oid::from_str(line) {
self.mandatory_ekus.insert(line.to_string());
}
}
}
pub fn clear_ekus(&mut self) {
self.additional_ekus.clear();
self.mandatory_ekus.clear();
}
pub fn clear(&mut self) {
self.trust_anchors.clear();
self.end_entity_cert_set.clear();
self.additional_ekus.clear();
self.mandatory_ekus.clear();
}
pub(crate) fn anchor_sets(&self) -> impl Iterator<Item = &'_ TrustAnchor> {
self.trust_anchors.iter()
}
pub(crate) fn get_anchor_set(&self, kind: TrustAnchorType, uri: &str) -> Option<&TrustAnchor> {
self.trust_anchors
.iter()
.find(|a| a.trust_anchor_type == kind && a.trust_anchor_uri == uri)
}
pub(crate) fn verify_mandatory_ekus(&self, eku: &ExtendedKeyUsage) -> bool {
for mandatory_oid in self.mandatory_ekus.iter() {
let Ok(mandatory_oid) = Oid::from_str(mandatory_oid) else {
return false;
};
let mandatory_oid_str = mandatory_oid.to_string();
if mandatory_oid == EMAIL_PROTECTION_OID {
if !eku.email_protection {
return false;
}
continue;
}
if mandatory_oid == TIMESTAMPING_OID {
if !eku.time_stamping {
return false;
}
continue;
}
if mandatory_oid == OCSP_SIGNING_OID {
if !eku.ocsp_signing {
return false;
}
continue;
}
let found = eku.other.iter().find(|extra_oid| {
let extra_oid_str = extra_oid.to_string();
extra_oid_str == mandatory_oid_str
});
if found.is_none() {
return false;
}
}
true
}
pub(crate) fn has_allowed_eku<'a>(&self, eku: &'a ExtendedKeyUsage) -> Option<Oid<'a>> {
if eku.email_protection {
return Some(EMAIL_PROTECTION_OID.clone());
}
if eku.time_stamping {
return Some(TIMESTAMPING_OID.clone());
}
if eku.ocsp_signing {
return Some(OCSP_SIGNING_OID.clone());
}
for extra_oid in eku.other.iter().as_ref() {
let extra_oid_str = extra_oid.to_string();
if self.additional_ekus.contains(&extra_oid_str) {
return Some(extra_oid.clone());
}
}
None
}
}
fn base64_sha256_cert_der(cert_der: &[u8]) -> String {
let cert_sha256 = sha256(cert_der);
base64::encode(&cert_sha256)
}
#[derive(Debug, Eq, Error, PartialEq)]
#[non_exhaustive]
pub enum CertificateTrustError {
#[error("the certificate is not trusted")]
CertificateNotTrusted,
#[error("a CA certificate in the chain is outside its validity period")]
CaCertificateOutsideValidity,
#[error("the certificate contains an invalid extended key usage (EKU) value")]
InvalidEku,
#[error("an error was reported by the cryptography library: {0}")]
CryptoLibraryError(String),
#[error("the certificate or certificate chain is invalid")]
InvalidCertificate,
#[error("internal error ({0})")]
InternalError(String),
}
#[cfg(all(
feature = "openssl",
not(all(feature = "rust_native_crypto", target_arch = "wasm32"))
))]
impl From<openssl::error::ErrorStack> for CertificateTrustError {
fn from(err: openssl::error::ErrorStack) -> Self {
Self::CryptoLibraryError(err.to_string())
}
}
#[cfg(all(
feature = "openssl",
not(all(feature = "rust_native_crypto", target_arch = "wasm32"))
))]
impl From<c2pa_raw_crypto::OpenSslMutexUnavailable> for CertificateTrustError {
fn from(err: c2pa_raw_crypto::OpenSslMutexUnavailable) -> Self {
Self::InternalError(err.to_string())
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct InvalidCertificateError(pub(crate) String);
impl fmt::Display for InvalidCertificateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Unable to parse certificate list: {}", self.0)
}
}
impl Error for InvalidCertificateError {}
static EMAIL_PROTECTION_OID: Oid<'static> = oid!(1.3.6 .1 .5 .5 .7 .3 .4);
static TIMESTAMPING_OID: Oid<'static> = oid!(1.3.6 .1 .5 .5 .7 .3 .8);
static OCSP_SIGNING_OID: Oid<'static> = oid!(1.3.6 .1 .5 .5 .7 .3 .9);
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
#![allow(clippy::panic)]
#![allow(clippy::unwrap_used)]
use asn1_rs::{oid, Oid};
use c2pa_macros::c2pa_test_async;
use c2pa_raw_crypto::SigningAlg;
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;
use x509_parser::{extensions::ExtendedKeyUsage, pem::Pem};
use crate::{
crypto::cose::{
CertificateTrustError, CertificateTrustPolicy, InvalidCertificateError, TrustAnchorType,
},
Settings,
};
fn test_cert_chain(alg: SigningAlg) -> Vec<Vec<u8>> {
let cert_chain: &[u8] = match alg {
SigningAlg::Ed25519 => {
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ed25519.pub")
}
SigningAlg::Es256 => {
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es256.pub")
}
SigningAlg::Es384 => {
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es384.pub")
}
SigningAlg::Es512 => {
include_bytes!("../../../tests/fixtures/crypto/raw_signature/es512.pub")
}
SigningAlg::Ps256 => {
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps256.pub")
}
SigningAlg::Ps384 => {
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps384.pub")
}
SigningAlg::Ps512 => {
include_bytes!("../../../tests/fixtures/crypto/raw_signature/ps512.pub")
}
_ => panic!("unsupported test signing algorithm: {alg}"),
};
#[allow(clippy::unwrap_used)]
crate::crypto::cert_chain_pem_to_der(cert_chain).unwrap()
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn impl_debug() {
let ctp = CertificateTrustPolicy::new();
let _ = format!("{ctp:#?}");
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn new() {
let ctp = CertificateTrustPolicy::new();
assert_eq!(
ctp.has_allowed_eku(&email_eku()).unwrap(),
EMAIL_PROTECTION_OID
);
assert!(ctp.has_allowed_eku(&document_signing_eku()).is_none());
assert_eq!(
ctp.has_allowed_eku(&time_stamping_eku()).unwrap(),
TIME_STAMPING_OID
);
assert_eq!(
ctp.has_allowed_eku(&ocsp_signing_eku()).unwrap(),
OCSP_SIGNING_OID
);
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn default() {
let ctp = CertificateTrustPolicy::default();
assert_eq!(
ctp.has_allowed_eku(&email_eku()).unwrap(),
EMAIL_PROTECTION_OID
);
assert_eq!(
ctp.has_allowed_eku(&document_signing_eku()).unwrap(),
DOCUMENT_SIGNING_OID
);
assert_eq!(
ctp.has_allowed_eku(&time_stamping_eku()).unwrap(),
TIME_STAMPING_OID
);
assert_eq!(
ctp.has_allowed_eku(&ocsp_signing_eku()).unwrap(),
OCSP_SIGNING_OID
);
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn clear() {
let mut ctp = CertificateTrustPolicy::default();
ctp.clear();
assert_eq!(
ctp.has_allowed_eku(&email_eku()).unwrap(),
EMAIL_PROTECTION_OID
);
assert!(ctp.has_allowed_eku(&document_signing_eku()).is_none());
assert_eq!(
ctp.has_allowed_eku(&time_stamping_eku()).unwrap(),
TIME_STAMPING_OID
);
assert_eq!(
ctp.has_allowed_eku(&ocsp_signing_eku()).unwrap(),
OCSP_SIGNING_OID
);
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn add_valid_ekus_err_bad_utf8() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_valid_ekus(&[128, 0]);
assert_eq!(
ctp.has_allowed_eku(&email_eku()).unwrap(),
EMAIL_PROTECTION_OID
);
assert!(ctp.has_allowed_eku(&document_signing_eku()).is_none());
assert_eq!(
ctp.has_allowed_eku(&time_stamping_eku()).unwrap(),
TIME_STAMPING_OID
);
assert_eq!(
ctp.has_allowed_eku(&ocsp_signing_eku()).unwrap(),
OCSP_SIGNING_OID
);
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn add_trust_anchors_err_bad_pem() {
let mut ctp = CertificateTrustPolicy::new();
assert!(ctp
.add_trust_anchors(BAD_PEM.as_bytes(), "", TrustAnchorType::Manifest, None)
.is_err());
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn add_end_entity_credentials_err_bad_pem() {
let mut ctp = CertificateTrustPolicy::new();
assert!(ctp.add_end_entity_credentials(BAD_PEM.as_bytes()).is_err());
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn err_to_string() {
let ice = InvalidCertificateError("foo".to_string());
assert_eq!(ice.to_string(), "Unable to parse certificate list: foo");
}
#[test]
#[cfg_attr(
all(target_arch = "wasm32", not(target_os = "wasi")),
wasm_bindgen_test
)]
fn err_debug() {
let ice = InvalidCertificateError("foo".to_string());
assert_eq!(
format!("{ice:#?}"),
"InvalidCertificateError(\n \"foo\",\n)"
);
}
fn email_eku() -> ExtendedKeyUsage<'static> {
ExtendedKeyUsage {
any: false,
server_auth: false,
client_auth: false,
code_signing: false,
email_protection: true,
time_stamping: false,
ocsp_signing: false,
other: vec![],
}
}
fn document_signing_eku() -> ExtendedKeyUsage<'static> {
ExtendedKeyUsage {
any: false,
server_auth: false,
client_auth: false,
code_signing: false,
email_protection: false,
time_stamping: false,
ocsp_signing: false,
other: vec![DOCUMENT_SIGNING_OID.clone()],
}
}
fn time_stamping_eku() -> ExtendedKeyUsage<'static> {
ExtendedKeyUsage {
any: false,
server_auth: false,
client_auth: false,
code_signing: false,
email_protection: false,
time_stamping: true,
ocsp_signing: false,
other: vec![],
}
}
fn ocsp_signing_eku() -> ExtendedKeyUsage<'static> {
ExtendedKeyUsage {
any: false,
server_auth: false,
client_auth: false,
code_signing: false,
email_protection: false,
time_stamping: false,
ocsp_signing: true,
other: vec![],
}
}
static EMAIL_PROTECTION_OID: Oid<'static> = oid!(1.3.6 .1 .5 .5 .7 .3 .4);
static DOCUMENT_SIGNING_OID: Oid<'static> = oid!(1.3.6 .1 .5 .5 .7 .3 .36);
static TIME_STAMPING_OID: Oid<'static> = oid!(1.3.6 .1 .5 .5 .7 .3 .8);
static OCSP_SIGNING_OID: Oid<'static> = oid!(1.3.6 .1 .5 .5 .7 .3 .9);
static BAD_PEM: &str = r#"
-----BEGIN CERTIFICATE-----
µIICEzCCAcWgAwIBAgIUW4fUnS38162x10PCnB8qFsrQuZgwBQYDK2VwMHcxCzAJ
BgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJlMRowGAYD
VQQKDBFDMlBBIFRlc3QgUm9vdCBDQTEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05M
WTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yMjA2MTAxODQ2NDFaFw0zMjA2MDcxODQ2
NDFaMHcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdo
ZXJlMRowGAYDVQQKDBFDMlBBIFRlc3QgUm9vdCBDQTEZMBcGA1UECwwQRk9SIFRF
U1RJTkdfT05MWTEQMA4GA1UEAwwHUm9vdCBDQTAqMAUGAytlcAMhAGPUgK9q1H3D
eKMGqLGjTXJSpsrLpe0kpxkaFMe7KUAuo2MwYTAdBgNVHQ4EFgQUXuZWArP1jiRM
fgye6ZqRyGupTowwHwYDVR0jBBgwFoAUXuZWArP1jiRMfgye6ZqRyGupTowwDwYD
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwBQYDK2VwA0EA8E79g54u2fUy
dfVLPyqKmtjenOUMvVQD7waNbetLY7kvUJZCd5eaDghk30/Q1RaNjiP/2RfA/it8
zGxQnM2hCA==
-----END CERTIFICATE-----
"#;
#[test]
fn test_system_trust_store() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_trust_anchors(
include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/test_cert_root_bundle.pem"
),
"",
TrustAnchorType::Manifest,
None,
)
.unwrap();
let ps256 = test_cert_chain(SigningAlg::Ps256);
let ps384 = test_cert_chain(SigningAlg::Ps384);
let ps512 = test_cert_chain(SigningAlg::Ps512);
let es256 = test_cert_chain(SigningAlg::Es256);
let es384 = test_cert_chain(SigningAlg::Es384);
let es512 = test_cert_chain(SigningAlg::Es512);
let ed25519 = test_cert_chain(SigningAlg::Ed25519);
let ps256_certs = ps256.clone();
let ps384_certs = ps384.clone();
let ps512_certs = ps512.clone();
let es256_certs = es256.clone();
let es384_certs = es384.clone();
let es512_certs = es512.clone();
let ed25519_certs = ed25519.clone();
assert!(
ctp.check_certificate_trust(&ps256_certs[1..], &ps256_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&ps384_certs[1..], &ps384_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&ps512_certs[1..], &ps512_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&es256_certs[1..], &es256_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&es384_certs[1..], &es384_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&es512_certs[1..], &es512_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&ed25519_certs[1..], &ed25519_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
}
#[test]
fn test_intermediate_validity_is_checked() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_trust_anchors(
include_bytes!("../../../tests/fixtures/crypto/cose/chain_trust_root.pub"),
"http://some_trust_list",
TrustAnchorType::Manifest,
None,
)
.unwrap();
let valid_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/cose/chain_trust_valid.pub"
));
ctp.check_certificate_trust(&valid_certs[1..], &valid_certs[0], None)
.expect("a currently-valid, properly-signed intermediate should be trusted");
let expired_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/cose/chain_trust_expired_intermediate.pub"
));
assert_eq!(
ctp.check_certificate_trust(&expired_certs[1..], &expired_certs[0], None),
Err(CertificateTrustError::CaCertificateOutsideValidity),
"an expired intermediate CA certificate must not be trusted"
);
let mut unrelated = CertificateTrustPolicy::new();
unrelated
.add_trust_anchors(
include_bytes!("../../../tests/fixtures/crypto/cose/ca_authorization_root.pub"),
"http://some_trust_list",
TrustAnchorType::Manifest,
None,
)
.unwrap();
assert_eq!(
unrelated.check_certificate_trust(&expired_certs[1..], &expired_certs[0], None),
Err(CertificateTrustError::CertificateNotTrusted)
);
}
#[c2pa_macros::c2pa_test_async]
async fn test_not_yet_valid_intermediate_is_checked() {
use x509_parser::prelude::{FromDer, X509Certificate};
let mut ctp = CertificateTrustPolicy::new();
ctp.add_trust_anchors(
include_bytes!("../../../tests/fixtures/crypto/cose/chain_trust_future_root.pub"),
"http://some_trust_list",
TrustAnchorType::Manifest,
None,
)
.unwrap();
let certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/cose/chain_trust_future_intermediate.pub"
));
let (_, leaf) = X509Certificate::from_der(&certs[0]).unwrap();
let (_, intermediate) = X509Certificate::from_der(&certs[1]).unwrap();
let before = 1_788_220_800;
let check_time = x509_parser::time::ASN1Time::from_timestamp(before).unwrap();
assert!(leaf.validity().is_valid_at(check_time));
assert!(check_time < intermediate.validity().not_before);
assert_eq!(
ctp.check_certificate_trust(&certs[1..], &certs[0], Some(before)),
Err(CertificateTrustError::CaCertificateOutsideValidity)
);
assert_eq!(
ctp.check_certificate_trust_async(&certs[1..], &certs[0], Some(before))
.await,
Err(CertificateTrustError::CaCertificateOutsideValidity)
);
let after = 1_851_379_200;
assert_eq!(
ctp.check_certificate_trust(&certs[1..], &certs[0], Some(after))
.unwrap(),
(TrustAnchorType::Manifest, "http://some_trust_list".into())
);
assert_eq!(
ctp.check_certificate_trust_async(&certs[1..], &certs[0], Some(after))
.await
.unwrap(),
(TrustAnchorType::Manifest, "http://some_trust_list".into())
);
}
#[test]
fn test_non_ca_intermediate_is_rejected() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_trust_anchors(
include_bytes!("../../../tests/fixtures/crypto/cose/ca_authorization_root.pub"),
"http://some_trust_list",
TrustAnchorType::Manifest,
None,
)
.unwrap();
let certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/cose/ca_authorization_non_ca_intermediate.pub"
));
ctp.check_certificate_trust(&certs[1..], &certs[0], None)
.expect_err(
"a certificate without basicConstraints CA:TRUE must not be usable as an issuer",
);
}
#[test]
fn test_path_len_constraint_is_enforced() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_trust_anchors(
include_bytes!("../../../tests/fixtures/crypto/cose/ca_authorization_root.pub"),
"http://some_trust_list",
TrustAnchorType::Manifest,
None,
)
.unwrap();
let ok_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/cose/ca_authorization_pathlen_ok.pub"
));
ctp.check_certificate_trust(&ok_certs[1..], &ok_certs[0], None)
.expect("a chain respecting its pathLenConstraint should be trusted");
let violation_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/cose/ca_authorization_pathlen_violation.pub"
));
ctp.check_certificate_trust(&violation_certs[1..], &violation_certs[0], None)
.expect_err("a chain violating its pathLenConstraint must not be trusted");
}
#[test]
fn test_user_trust_store() {
let ctp = CertificateTrustPolicy::default();
let ps256 = test_cert_chain(SigningAlg::Ps256);
let ps384 = test_cert_chain(SigningAlg::Ps384);
let ps512 = test_cert_chain(SigningAlg::Ps512);
let es256 = test_cert_chain(SigningAlg::Es256);
let es384 = test_cert_chain(SigningAlg::Es384);
let es512 = test_cert_chain(SigningAlg::Es512);
let ed25519 = test_cert_chain(SigningAlg::Ed25519);
let ps256_certs = ps256.clone();
let ps384_certs = ps384.clone();
let ps512_certs = ps512.clone();
let es256_certs = es256.clone();
let es384_certs = es384.clone();
let es512_certs = es512.clone();
let ed25519_certs = ed25519.clone();
assert!(
ctp.check_certificate_trust(&ps256_certs[1..], &ps256_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&ps384_certs[1..], &ps384_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&ps512_certs[1..], &ps512_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&es256_certs[1..], &es256_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&es384_certs[1..], &es384_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&es512_certs[1..], &es512_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
assert!(
ctp.check_certificate_trust(&ed25519_certs[1..], &ed25519_certs[0], None)
.unwrap()
.0
== TrustAnchorType::Manifest
);
}
#[c2pa_test_async]
async fn test_trust_store_async() {
let ctp = CertificateTrustPolicy::default();
let ps256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps256.pub"
));
let ps384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps384.pub"
));
let ps512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps512.pub"
));
let es256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es256.pub"
));
let es384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es384.pub"
));
let es512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es512.pub"
));
let ed25519_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ed25519.pub"
));
ctp.check_certificate_trust_async(&ps256_certs[1..], &ps256_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&ps384_certs[1..], &ps384_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&ps512_certs[1..], &ps512_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&es256_certs[1..], &es256_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&es384_certs[1..], &es384_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&es512_certs[1..], &es512_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&ed25519_certs[1..], &ed25519_certs[0], None)
.await
.unwrap();
}
#[test]
fn test_broken_trust_chain() {
let ctp = CertificateTrustPolicy::default();
let ps256 = test_cert_chain(SigningAlg::Ps256);
let ps384 = test_cert_chain(SigningAlg::Ps384);
let ps512 = test_cert_chain(SigningAlg::Ps512);
let es256 = test_cert_chain(SigningAlg::Es256);
let es384 = test_cert_chain(SigningAlg::Es384);
let es512 = test_cert_chain(SigningAlg::Es512);
let ed25519 = test_cert_chain(SigningAlg::Ed25519);
let ps256_certs = ps256.clone();
let ps384_certs = ps384.clone();
let ps512_certs = ps512.clone();
let es256_certs = es256.clone();
let es384_certs = es384.clone();
let es512_certs = es512.clone();
let ed25519_certs = ed25519.clone();
assert_eq!(
ctp.check_certificate_trust(&ps256_certs[2..], &ps256_certs[0], None)
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust(&ps384_certs[2..], &ps384_certs[0], None)
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust(&ps384_certs[2..], &ps384_certs[0], None)
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust(&ps512_certs[2..], &ps512_certs[0], None)
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust(&es256_certs[2..], &es256_certs[0], None)
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust(&es384_certs[2..], &es384_certs[0], None)
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust(&es512_certs[2..], &es512_certs[0], None)
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust(&ed25519_certs[2..], &ed25519_certs[0], None)
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
}
#[c2pa_test_async]
async fn test_broken_trust_chain_async() {
let ctp = CertificateTrustPolicy::default();
let ps256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps256.pub"
));
let ps384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps384.pub"
));
let ps512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps512.pub"
));
let es256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es256.pub"
));
let es384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es384.pub"
));
let es512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es512.pub"
));
let ed25519_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ed25519.pub"
));
assert_eq!(
ctp.check_certificate_trust_async(&ps256_certs[2..], &ps256_certs[0], None)
.await
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust_async(&ps384_certs[2..], &ps384_certs[0], None)
.await
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust_async(&ps384_certs[2..], &ps384_certs[0], None)
.await
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust_async(&ps512_certs[2..], &ps512_certs[0], None)
.await
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust_async(&es256_certs[2..], &es256_certs[0], None)
.await
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust_async(&es384_certs[2..], &es384_certs[0], None)
.await
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust_async(&es512_certs[2..], &es512_certs[0], None)
.await
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
assert_eq!(
ctp.check_certificate_trust_async(&ed25519_certs[2..], &ed25519_certs[0], None)
.await
.unwrap_err(),
CertificateTrustError::CertificateNotTrusted
);
}
#[test]
fn test_allowed_list() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ed25519.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es256.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es384.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es512.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps256.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps384.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps512.pub"
))
.unwrap();
let ps256 = test_cert_chain(SigningAlg::Ps256);
let ps384 = test_cert_chain(SigningAlg::Ps384);
let ps512 = test_cert_chain(SigningAlg::Ps512);
let es256 = test_cert_chain(SigningAlg::Es256);
let es384 = test_cert_chain(SigningAlg::Es384);
let es512 = test_cert_chain(SigningAlg::Es512);
let ed25519 = test_cert_chain(SigningAlg::Ed25519);
let ps256_certs = ps256.clone();
let ps384_certs = ps384.clone();
let ps512_certs = ps512.clone();
let es256_certs = es256.clone();
let es384_certs = es384.clone();
let es512_certs = es512.clone();
let ed25519_certs = ed25519.clone();
ctp.check_certificate_trust(&ps256_certs[1..], &ps256_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ps384_certs[1..], &ps384_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ps512_certs[1..], &ps512_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es256_certs[1..], &es256_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es384_certs[1..], &es384_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es512_certs[1..], &es512_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ed25519_certs[1..], &ed25519_certs[0], None)
.unwrap();
}
#[c2pa_test_async]
async fn test_allowed_list_async() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ed25519.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es256.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es384.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es512.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps256.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps384.pub"
))
.unwrap();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps512.pub"
))
.unwrap();
let ps256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps256.pub"
));
let ps384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps384.pub"
));
let ps512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps512.pub"
));
let es256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es256.pub"
));
let es384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es384.pub"
));
let es512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es512.pub"
));
let ed25519_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ed25519.pub"
));
ctp.check_certificate_trust_async(&ps256_certs[1..], &ps256_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&ps384_certs[1..], &ps384_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&ps512_certs[1..], &ps512_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&es256_certs[1..], &es256_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&es384_certs[1..], &es384_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&es512_certs[1..], &es512_certs[0], None)
.await
.unwrap();
ctp.check_certificate_trust_async(&ed25519_certs[1..], &ed25519_certs[0], None)
.await
.unwrap();
}
#[test]
fn test_allowed_list_hashes() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/allowed_list.hash"
))
.unwrap();
let ps256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps256.pub"
));
let ps384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps384.pub"
));
let ps512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps512.pub"
));
let es256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es256.pub"
));
let es384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es384.pub"
));
let es512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es512.pub"
));
let ed25519_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ed25519.pub"
));
ctp.check_certificate_trust(&ps256_certs[1..], &ps256_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ps384_certs[1..], &ps384_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ps512_certs[1..], &ps512_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es256_certs[1..], &es256_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es384_certs[1..], &es384_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es512_certs[1..], &es512_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ed25519_certs[1..], &ed25519_certs[0], None)
.unwrap();
}
#[c2pa_test_async]
async fn test_allowed_list_hashes_async() {
let mut ctp = CertificateTrustPolicy::new();
ctp.add_end_entity_credentials(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/allowed_list.hash"
))
.unwrap();
let ps256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps256.pub"
));
let ps384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps384.pub"
));
let ps512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ps512.pub"
));
let es256_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es256.pub"
));
let es384_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es384.pub"
));
let es512_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/es512.pub"
));
let ed25519_certs = cert_ders_from_pem(include_bytes!(
"../../../tests/fixtures/crypto/raw_signature/ed25519.pub"
));
ctp.check_certificate_trust(&ps256_certs[1..], &ps256_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ps384_certs[1..], &ps384_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ps512_certs[1..], &ps512_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es256_certs[1..], &es256_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es384_certs[1..], &es384_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&es512_certs[1..], &es512_certs[0], None)
.unwrap();
ctp.check_certificate_trust(&ed25519_certs[1..], &ed25519_certs[0], None)
.unwrap();
}
fn cert_ders_from_pem(cert_chain: &[u8]) -> Vec<Vec<u8>> {
Pem::iter_from_buffer(cert_chain)
.map(|r| r.unwrap().contents)
.collect()
}
#[test]
fn test_multiple_trust_list_fetch() {
let settings = Settings::default();
let ta = settings.trust.anchors.unwrap().first().unwrap().clone();
let mut ctp = CertificateTrustPolicy::new();
ctp.add_trust_anchors(
ta.trust_anchors.as_bytes(),
"https://example.com/tsa",
TrustAnchorType::TSA,
None,
)
.unwrap();
ctp.add_trust_anchors(
ta.trust_anchors.as_bytes(),
"https://example.com/cawg",
TrustAnchorType::CAWG,
None,
)
.unwrap();
assert!(!ctp.tsa_trust_anchors().is_empty());
assert!(!ctp.cawg_trust_anchors().is_empty());
assert!(ctp.signing_trust_anchors().is_empty());
}
#[test]
fn test_ctp_has_mandatory_eku() {
let mut ctp = CertificateTrustPolicy::default();
assert!(ctp.verify_mandatory_ekus(&email_eku()));
assert!(ctp.verify_mandatory_ekus(&document_signing_eku()));
assert!(ctp.verify_mandatory_ekus(&time_stamping_eku()));
assert!(ctp.verify_mandatory_ekus(&ocsp_signing_eku()));
ctp.clear_ekus();
ctp.add_mandatory_ekus(EMAIL_PROTECTION_OID.to_string().as_bytes());
assert!(ctp.verify_mandatory_ekus(&email_eku()));
assert!(!ctp.verify_mandatory_ekus(&document_signing_eku()));
assert!(!ctp.verify_mandatory_ekus(&time_stamping_eku()));
assert!(!ctp.verify_mandatory_ekus(&ocsp_signing_eku()));
let mandatory_ekus = format!("{}\n{}\n", DOCUMENT_SIGNING_OID, EMAIL_PROTECTION_OID);
let eku = ExtendedKeyUsage {
any: false,
server_auth: false,
client_auth: false,
code_signing: false,
email_protection: true,
time_stamping: false,
ocsp_signing: false,
other: vec![DOCUMENT_SIGNING_OID.clone()],
};
ctp.add_mandatory_ekus(mandatory_ekus.as_bytes());
assert!(!ctp.verify_mandatory_ekus(&email_eku()));
assert!(!ctp.verify_mandatory_ekus(&document_signing_eku()));
assert!(!ctp.verify_mandatory_ekus(&time_stamping_eku()));
assert!(!ctp.verify_mandatory_ekus(&ocsp_signing_eku()));
assert!(ctp.verify_mandatory_ekus(&eku));
let mandatory_ekus = format!(
"{}\n{}\n{}\n",
DOCUMENT_SIGNING_OID, EMAIL_PROTECTION_OID, TIME_STAMPING_OID
);
let eku = ExtendedKeyUsage {
any: false,
server_auth: false,
client_auth: false,
code_signing: false,
email_protection: true,
time_stamping: true,
ocsp_signing: false,
other: vec![DOCUMENT_SIGNING_OID.clone()],
};
ctp.add_mandatory_ekus(mandatory_ekus.as_bytes());
assert!(!ctp.verify_mandatory_ekus(&email_eku()));
assert!(!ctp.verify_mandatory_ekus(&document_signing_eku()));
assert!(!ctp.verify_mandatory_ekus(&time_stamping_eku()));
assert!(!ctp.verify_mandatory_ekus(&ocsp_signing_eku()));
assert!(ctp.verify_mandatory_ekus(&eku));
let mandatory_ekus = format!(
"{}\n{}\n{}\n{}\n",
DOCUMENT_SIGNING_OID, EMAIL_PROTECTION_OID, TIME_STAMPING_OID, OCSP_SIGNING_OID
);
let eku = ExtendedKeyUsage {
any: false,
server_auth: false,
client_auth: false,
code_signing: false,
email_protection: true,
time_stamping: true,
ocsp_signing: true,
other: vec![DOCUMENT_SIGNING_OID.clone()],
};
ctp.add_mandatory_ekus(mandatory_ekus.as_bytes());
assert!(!ctp.verify_mandatory_ekus(&email_eku()));
assert!(!ctp.verify_mandatory_ekus(&document_signing_eku()));
assert!(!ctp.verify_mandatory_ekus(&time_stamping_eku()));
assert!(!ctp.verify_mandatory_ekus(&ocsp_signing_eku()));
assert!(ctp.verify_mandatory_ekus(&eku));
let eku = ExtendedKeyUsage {
any: false,
server_auth: true,
client_auth: true,
code_signing: true,
email_protection: true,
time_stamping: true,
ocsp_signing: true,
other: vec![DOCUMENT_SIGNING_OID.clone()],
};
assert!(ctp.verify_mandatory_ekus(&eku));
}
}