diff --git a/Cargo.lock b/Cargo.lock index 46d40bbee..1fa741fcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -692,7 +692,6 @@ dependencies = [ "id3", "image 0.24.9", "img-parts", - "instant", "jfifdump", "js-sys", "jumbf", @@ -702,7 +701,6 @@ dependencies = [ "memchr", "mockall", "mp4", - "openssl", "pem 3.0.4", "png_pong", "rand", @@ -763,6 +761,7 @@ dependencies = [ "getrandom", "hex", "js-sys", + "nom", "openssl", "p256", "p384", @@ -2256,18 +2255,6 @@ dependencies = [ "serde", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "ipnet" version = "2.10.1" diff --git a/deny.toml b/deny.toml index 02c42d75b..c69a9bf7d 100644 --- a/deny.toml +++ b/deny.toml @@ -17,7 +17,6 @@ yanked = "deny" ignore = [ "RUSTSEC-2021-0127", # serde_cbor "RUSTSEC-2023-0071", # rsa Marvin Attack: (https://jira.corp.adobe.com/browse/CAI-5104) - "RUSTSEC-2024-0384", # instant (https://github.com/contentauth/c2pa-rs/issues/663) "RUSTSEC-2024-0399", # tokio-rustls server: https://rustsec.org/advisories/RUSTSEC-2024-0399 ] diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index d07b165fa..a98bdc982 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -42,6 +42,7 @@ const-hex = "1.14" coset = "0.3.1" getrandom = { version = "0.2.7", features = ["js"] } hex = "0.4.3" +nom = "7.1.3" rand = "0.8.5" rasn = "0.22.0" rasn-ocsp = "0.22.0" @@ -52,6 +53,7 @@ serde_bytes = "0.11.5" sha1 = "0.10.6" sha2 = "0.10.6" thiserror = "2.0.8" +web-time = "1.1" x509-certificate = "0.21.0" x509-parser = "0.16.0" @@ -91,7 +93,6 @@ web-sys = { version = "0.3.58", features = [ "Window", "WorkerGlobalScope", ] } -web-time = "1.1" [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] getrandom = { version = "0.2.7", features = ["js"] } diff --git a/internal/crypto/src/cose/certificate_profile.rs b/internal/crypto/src/cose/certificate_profile.rs new file mode 100644 index 000000000..e011295e6 --- /dev/null +++ b/internal/crypto/src/cose/certificate_profile.rs @@ -0,0 +1,517 @@ +// Copyright 2022 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use asn1_rs::{Any, Class, FromDer, Header, Tag}; +use c2pa_status_tracker::{log_item, validation_codes::*, StatusTracker}; +use chrono::{DateTime, Utc}; +use thiserror::Error; +use web_time::SystemTime; +use x509_certificate::asn1time::GeneralizedTime; +use x509_parser::{ + certificate::{BasicExtension, X509Certificate}, + der_parser::{ber::parse_ber_sequence, oid}, + extensions::ParsedExtension, + oid_registry::Oid, + x509::{AlgorithmIdentifier, X509Version}, +}; + +use crate::{asn1::rfc3161::TstInfo, cose::CertificateTrustPolicy}; + +/// Verify that an X.509 certificate meets the requirements stated in [§14.5.1, +/// Certificate Profiles]. +/// +/// [§14.5.1, Certificate Profiles]: https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_certificate_profiles +pub fn check_certificate_profile( + certificate_der: &[u8], + ctp: &CertificateTrustPolicy, + validation_log: &mut impl StatusTracker, + _tst_info_opt: Option<&TstInfo>, +) -> Result<(), CertificateProfileError> { + let (_rem, signcert) = X509Certificate::from_der(certificate_der).map_err(|_err| { + log_item!( + "Cose_Sign1", + "certificate could not be parsed", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + CertificateProfileError::InvalidCertificate + })?; + + // Version shall be v3 as per RFC 5280, section 4.1.2.1. + if signcert.version() != X509Version::V3 { + log_item!( + "Cose_Sign1", + "certificate version incorrect", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw( + validation_log, + CertificateProfileError::InvalidCertificateVersion, + ); + + return Err(CertificateProfileError::InvalidCertificateVersion); + } + + // Was the certificate valid at time of signing? + if let Some(tst_info) = _tst_info_opt { + // A valid time stamp was associated with this signature: Ensure that the + // timestamp was valid at that time. + let signing_time = generalized_time_to_datetime(tst_info.gen_time.clone()); + if !signcert.validity().is_valid_at( + x509_parser::time::ASN1Time::from_timestamp(signing_time.timestamp()) + .map_err(|_| CertificateProfileError::InvalidCertificate)?, + ) { + log_item!("Cose_Sign1", "certificate expired", "check_cert_alg") + .validation_status(SIGNING_CREDENTIAL_EXPIRED) + .failure_no_throw( + validation_log, + CertificateProfileError::CertificateNotValidAtTime, + ); + + return Err(CertificateProfileError::CertificateNotValidAtTime); + } + } else { + // No valid time stamp was associated with this signature: Ensure that the + // timestamp is valid now. + let Ok(now) = SystemTime::now().duration_since(web_time::UNIX_EPOCH) else { + return Err(CertificateProfileError::InternalError( + "system time invalid", + )); + }; + + if !signcert.validity().is_valid_at( + x509_parser::time::ASN1Time::from_timestamp(now.as_secs() as i64) + .map_err(|_| CertificateProfileError::InvalidCertificate)?, + ) { + log_item!("Cose_Sign1", "certificate expired", "check_cert_alg") + .validation_status(SIGNING_CREDENTIAL_EXPIRED) + .failure_no_throw( + validation_log, + CertificateProfileError::CertificateNotValidAtTime, + ); + + return Err(CertificateProfileError::CertificateNotValidAtTime); + } + } + + let cert_alg = &signcert.signature_algorithm.algorithm; + + // Certificate must be signed with one of the following algorithms. + if !(*cert_alg == SHA256_WITH_RSAENCRYPTION_OID + || *cert_alg == SHA384_WITH_RSAENCRYPTION_OID + || *cert_alg == SHA512_WITH_RSAENCRYPTION_OID + || *cert_alg == ECDSA_WITH_SHA256_OID + || *cert_alg == ECDSA_WITH_SHA384_OID + || *cert_alg == ECDSA_WITH_SHA512_OID + || *cert_alg == RSASSA_PSS_OID + || *cert_alg == ED25519_OID) + { + log_item!( + "Cose_Sign1", + "certificate algorithm not supported", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw( + validation_log, + CertificateProfileError::UnsupportedAlgorithm, + ); + + return Err(CertificateProfileError::UnsupportedAlgorithm); + } + + // Verify RSA_PSS parameters. + if *cert_alg == RSASSA_PSS_OID { + if let Some(parameters) = &signcert.signature_algorithm.parameters { + let seq = parameters + .as_sequence() + .map_err(|_err| CertificateProfileError::InvalidCertificate)?; + + let (_i, (ha_alg, mgf_ai)) = seq + .parse(|i| { + let (i, h) =
::from_der(i)?; + if h.class() != Class::ContextSpecific || h.tag() != Tag(0) { + return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); + } + + let (i, ha_alg) = AlgorithmIdentifier::from_der(i) + .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; + + let (i, h) =
::from_der(i)?; + if h.class() != Class::ContextSpecific || h.tag() != Tag(1) { + return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); + } + + let (i, mgf_ai) = AlgorithmIdentifier::from_der(i) + .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; + + // Ignore anything that follows these two parameters. + + Ok((i, (ha_alg, mgf_ai))) + }) + .map_err(|_| CertificateProfileError::InvalidCertificate)?; + + let mgf_ai_parameters = mgf_ai + .parameters + .ok_or(CertificateProfileError::InvalidCertificate)?; + let mgf_ai_parameters = mgf_ai_parameters + .as_sequence() + .map_err(|_| CertificateProfileError::InvalidCertificate)?; + + let (_i, mgf_ai_params_algorithm) = + ::from_der(&mgf_ai_parameters.content) + .map_err(|_| CertificateProfileError::InvalidCertificate)?; + + let mgf_ai_params_algorithm = mgf_ai_params_algorithm + .as_oid() + .map_err(|_| CertificateProfileError::InvalidCertificate)?; + + if ha_alg.algorithm.to_id_string() != mgf_ai_params_algorithm.to_id_string() { + log_item!( + "Cose_Sign1", + "certificate algorithm error", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + + // check for one of the mandatory types + if !(ha_alg.algorithm == SHA256_OID + || ha_alg.algorithm == SHA384_OID + || ha_alg.algorithm == SHA512_OID) + { + log_item!( + "Cose_Sign1", + "certificate hash algorithm not supported", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + } else { + log_item!( + "Cose_Sign1", + "certificate missing algorithm parameters", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + } + + // CHeck curves for SPKI EC algorithms. + let pk = signcert.public_key(); + let skpi_alg = &pk.algorithm; + + if skpi_alg.algorithm == EC_PUBLICKEY_OID { + if let Some(parameters) = &skpi_alg.parameters { + let named_curve_oid = parameters + .as_oid() + .map_err(|_err| CertificateProfileError::InvalidCertificate)?; + + // Must be one of these named curves. + if !(named_curve_oid == PRIME256V1_OID + || named_curve_oid == SECP384R1_OID + || named_curve_oid == SECP521R1_OID) + { + log_item!( + "Cose_Sign1", + "certificate unsupported EC curve", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + } else { + return Err(CertificateProfileError::InvalidCertificate); + } + } + + // Check modulus minimum length for RSA & PSS algorithms. + if skpi_alg.algorithm == RSA_OID || skpi_alg.algorithm == RSASSA_PSS_OID { + let (_, skpi_ber) = parse_ber_sequence(&pk.subject_public_key.data) + .map_err(|_err| CertificateProfileError::InvalidCertificate)?; + + let seq = skpi_ber + .as_sequence() + .map_err(|_err| CertificateProfileError::InvalidCertificate)?; + if seq.len() < 2 { + return Err(CertificateProfileError::InvalidCertificate); + } + + let modulus = seq[0] + .as_bigint() + .map_err(|_| CertificateProfileError::InvalidCertificate)?; + + if modulus.bits() < 2048 { + log_item!( + "Cose_Sign1", + "certificate key length too short", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + } + + let tbscert = &signcert.tbs_certificate; + + // Disallow self-signed certificates. + if tbscert.is_ca() && tbscert.issuer() == tbscert.subject() { + log_item!( + "Cose_Sign1", + "certificate issuer and subject cannot be the same (self-signed disallowed)", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw( + validation_log, + CertificateProfileError::SelfSignedCertificate, + ); + + return Err(CertificateProfileError::SelfSignedCertificate); + } + + // Disallow unique IDs. + if signcert.issuer_uid.is_some() || signcert.subject_uid.is_some() { + log_item!( + "Cose_Sign1", + "certificate issuer/subject unique ids are not allowed", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + + let mut aki_good = false; + let mut ski_good = false; + let mut key_usage_good = false; + let mut handled_all_critical = true; + let extended_key_usage_good = match tbscert + .extended_key_usage() + .map_err(|_| CertificateProfileError::InvalidCertificate)? + { + Some(BasicExtension { value: eku, .. }) => { + if eku.any { + log_item!( + "Cose_Sign1", + "certificate 'any' EKU not allowed", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + + if ctp.has_allowed_eku(eku).is_none() { + log_item!( + "Cose_Sign1", + "certificate missing required EKU", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + + // one or the other || either of these two, and no others field + if (eku.ocsp_signing && eku.time_stamping) + || ((eku.ocsp_signing ^ eku.time_stamping) + && (eku.client_auth + | eku.code_signing + | eku.email_protection + | eku.server_auth + | !eku.other.is_empty())) + { + log_item!( + "Cose_Sign1", + "certificate invalid set of EKUs", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + return Err(CertificateProfileError::InvalidCertificate); + } + + true + } + + None => tbscert.is_ca(), // if is not ca it must be present + }; + + // Populate needed extension info. + for e in signcert.extensions() { + match e.parsed_extension() { + ParsedExtension::AuthorityKeyIdentifier(_aki) => { + aki_good = true; + } + ParsedExtension::SubjectKeyIdentifier(_spki) => { + ski_good = true; + } + ParsedExtension::KeyUsage(ku) => { + if ku.digital_signature() { + if ku.key_cert_sign() && !tbscert.is_ca() { + log_item!( + "Cose_Sign1", + "certificate missing digitalSignature EKU", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw( + validation_log, + CertificateProfileError::InvalidCertificate, + ); + + return Err(CertificateProfileError::InvalidCertificate); + } + key_usage_good = true; + } + if ku.key_cert_sign() || ku.non_repudiation() { + key_usage_good = true; + } + + // TO DO: warn if not marked critical. + // if !e.critical { // warn here somehow} + } + + ParsedExtension::CertificatePolicies(_) => (), + ParsedExtension::PolicyMappings(_) => (), + ParsedExtension::SubjectAlternativeName(_) => (), + ParsedExtension::BasicConstraints(_) => (), + ParsedExtension::NameConstraints(_) => (), + ParsedExtension::PolicyConstraints(_) => (), + ParsedExtension::ExtendedKeyUsage(_) => (), + ParsedExtension::CRLDistributionPoints(_) => (), + ParsedExtension::InhibitAnyPolicy(_) => (), + ParsedExtension::AuthorityInfoAccess(_) => (), + ParsedExtension::NSCertType(_) => (), + ParsedExtension::CRLNumber(_) => (), + ParsedExtension::ReasonCode(_) => (), + ParsedExtension::InvalidityDate(_) => (), + + ParsedExtension::Unparsed => { + if e.critical { + // Unhandled critical extension. + handled_all_critical = false; + } + } + + _ => { + if e.critical { + // Unhandled critical extension. + handled_all_critical = false; + } + } + } + } + + // If cert is a CA must have valid SubjectKeyIdentifier. + ski_good = if tbscert.is_ca() { ski_good } else { true }; + + // Check all flags. + if aki_good && ski_good && key_usage_good && extended_key_usage_good && handled_all_critical { + Ok(()) + } else { + log_item!( + "Cose_Sign1", + "certificate params incorrect", + "check_cert_alg" + ) + .validation_status(SIGNING_CREDENTIAL_INVALID) + .failure_no_throw(validation_log, CertificateProfileError::InvalidCertificate); + + Err(CertificateProfileError::InvalidCertificate) + } +} + +/// Describes errors that can be identified when checking a certificate's +/// profile. +#[derive(Debug, Eq, Error, PartialEq)] +#[non_exhaustive] +pub enum CertificateProfileError { + /// The certificate (or certificate chain) that was presented is invalid. + /// + /// This often occurs when the data presented isn't a valid X.509 + /// certificate in DER format. + #[error("the certificate is invalid")] + InvalidCertificate, + + /// The certificate must be a version 3 certificate per [RFC 5280, section + /// 4.1.2.1]. + /// + /// [RFC 5280, section 4.1.2.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.1 + #[error("the certificate must be a `v3` certificate")] + InvalidCertificateVersion, + + /// The certificate was used outside of its period of validity. + #[error("the certificate was not valid at time of signing")] + CertificateNotValidAtTime, + + /// The certificate was signed with an unsupported algorithm. + #[error("the certificate was signed with an unsupported algorithm")] + UnsupportedAlgorithm, + + /// The certificate contains an invalid extended key usage (EKU) value. + #[error("the certificate contains an invalid extended key usage (EKU) value")] + InvalidEku, + + /// The certificate was signed by the same entity as the certificate itself. + #[error("the certificate was self-signed")] + SelfSignedCertificate, + + /// An unexpected internal error occured while requesting the time stamp + /// response. + #[error("internal error ({0})")] + InternalError(&'static str), +} + +fn generalized_time_to_datetime(gt: GeneralizedTime) -> DateTime { + gt.into() +} + +const RSA_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .1); +const EC_PUBLICKEY_OID: Oid<'static> = oid!(1.2.840 .10045 .2 .1); +const RSASSA_PSS_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .10); + +const ECDSA_WITH_SHA256_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .2); +const ECDSA_WITH_SHA384_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .3); +const ECDSA_WITH_SHA512_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .4); +const SHA256_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .11); +const SHA384_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .12); +const SHA512_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .13); +const ED25519_OID: Oid<'static> = oid!(1.3.101 .112); +const SHA256_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .1); +const SHA384_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .2); +const SHA512_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .3); +const SECP521R1_OID: Oid<'static> = oid!(1.3.132 .0 .35); +const SECP384R1_OID: Oid<'static> = oid!(1.3.132 .0 .34); +const PRIME256V1_OID: Oid<'static> = oid!(1.2.840 .10045 .3 .1 .7); diff --git a/internal/crypto/src/cose/certificate_acceptance_policy.rs b/internal/crypto/src/cose/certificate_trust_policy.rs similarity index 57% rename from internal/crypto/src/cose/certificate_acceptance_policy.rs rename to internal/crypto/src/cose/certificate_trust_policy.rs index 55b50ee8d..30e668b45 100644 --- a/internal/crypto/src/cose/certificate_acceptance_policy.rs +++ b/internal/crypto/src/cose/certificate_trust_policy.rs @@ -1,4 +1,4 @@ -// Copyright 2023 Adobe. All rights reserved. +// Copyright 2024 Adobe. All rights reserved. // This file is licensed to you under the Apache License, // Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) // or the MIT license (http://opensource.org/licenses/MIT), @@ -13,30 +13,36 @@ #![allow(clippy::doc_lazy_continuation)] // Clippy and rustfmt aren't agreeing at the moment. :-( -use std::{collections::HashSet, error::Error, fmt, str::FromStr}; +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}; -/// A `CertificateAcceptancePolicy` retains information about trust anchors and -/// allowed EKUs to be used when verifying C2PA signing certificates. +use crate::{base64, hash::sha256}; + +/// A `CertificateTrustPolicy` is configured with information about trust +/// anchors, privately-accepted end-entity certificates, and allowed EKUs. It +/// can be used to evaluate a signing certificate against those policies. #[derive(Debug)] -pub struct CertificateAcceptancePolicy { +pub struct CertificateTrustPolicy { /// Trust anchors (root X.509 certificates) in DER format. trust_anchor_ders: Vec>, - /// End-entity certificaters (root X.509 certificates) in DER format. - end_entity_cert_ders: Vec>, + /// Base-64 encoded SHA-256 hash of end-entity certificates (root X.509 + /// certificates) in DER format. + end_entity_cert_set: HashSet, /// Additional extended key usage (EKU) OIDs. additional_ekus: HashSet, } -impl Default for CertificateAcceptancePolicy { +impl Default for CertificateTrustPolicy { fn default() -> Self { - let mut this = CertificateAcceptancePolicy { + let mut this = CertificateTrustPolicy { trust_anchor_ders: vec![], - end_entity_cert_ders: vec![], + end_entity_cert_set: HashSet::default(), additional_ekus: HashSet::default(), }; @@ -54,7 +60,7 @@ impl Default for CertificateAcceptancePolicy { } } -impl CertificateAcceptancePolicy { +impl CertificateTrustPolicy { /// Create a new certificate acceptance policy with no preconfigured trust /// roots. /// @@ -62,13 +68,66 @@ impl CertificateAcceptancePolicy { /// /// [`default()`]: Self::default() pub fn new() -> Self { - CertificateAcceptancePolicy { + CertificateTrustPolicy { trust_anchor_ders: vec![], - end_entity_cert_ders: vec![], + end_entity_cert_set: HashSet::default(), additional_ekus: HashSet::default(), } } + /// Evaluate a certificate against the trust policy described by this + /// struct. + /// + /// Returns `Ok(())` if the certificate appears on the end-entity + /// certificate list or has a valid chain to one of the trust anchors that + /// was provided and that it has a valid extended key usage (EKU). + /// + /// If `signing_time_epoch` is provided, evaluates the signing time (which + /// must be in Unix seconds since the epoch) against the certificate's + /// period of validity. + #[allow(unused)] // parameters may be unused in some cases + #[async_generic] + pub fn check_certificate_trust( + &self, + chain_der: &[Vec], + end_entity_cert_der: &[u8], + signing_time_epoch: Option, + ) -> Result<(), CertificateTrustError> { + // First check to see if the certificate appears in the allowed set of + // end-entity certificates. + let cert_hash = base64_sha256_cert_der(end_entity_cert_der); + if self.end_entity_cert_set.contains(&cert_hash) { + return Ok(()); + } + + if _async { + #[cfg(target_arch = "wasm32")] + { + return crate::webcrypto::check_certificate_trust::check_certificate_trust( + self, + chain_der, + end_entity_cert_der, + signing_time_epoch, + ) + .await; + } + } + + #[cfg(feature = "openssl")] + { + return crate::openssl::check_certificate_trust::check_certificate_trust( + self, + chain_der, + end_entity_cert_der, + signing_time_epoch, + ); + } + + Err(CertificateTrustError::InternalError( + "no implementation for certificate evaluation available", + )) + } + /// Add trust anchors (root X.509 certificates) that shall be accepted when /// verifying COSE signatures. /// @@ -134,16 +193,39 @@ impl CertificateAcceptancePolicy { /// certificates, regardless of how they may or may not chain up to other /// trust anchors. /// + /// As an optimization, this function also accepts standalone lines (outside + /// of the X.509 PEM blocks). Each such line must contain a Base-64 encoded + /// SHA_256 hash value over the value of a PEM certificate. + /// + /// Lines that match neither format (PEM or hash) are ignored. + /// /// [§14.4.3, Private Credential Storage]: https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_private_credential_storage 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) { // NOTE: The `x509_parser::pem::Pem` struct's `contents` field contains the // decoded PEM content, which is expected to be in DER format. match maybe_pem { - Ok(pem) => self.end_entity_cert_ders.push(pem.contents), + Ok(pem) => { + self.end_entity_cert_set + .insert(base64_sha256_cert_der(&pem.contents)); + } Err(e) => { return Err(InvalidCertificateError(e.to_string())); } @@ -196,26 +278,19 @@ impl CertificateAcceptancePolicy { /// configured. pub fn clear(&mut self) { self.trust_anchor_ders.clear(); - self.end_entity_cert_ders.clear(); + self.end_entity_cert_set.clear(); self.additional_ekus.clear(); } /// Return an iterator over the trust anchors. /// /// Each anchor will be returned in DER format. - pub fn trust_anchor_ders(&self) -> impl Iterator> { + pub(crate) fn trust_anchor_ders(&self) -> impl Iterator> { self.trust_anchor_ders.iter() } - /// Return an iterator over the allowed end-entity certificates. - /// - /// Each end-entity certificate will be returned in DER format. - pub fn end_entity_cert_ders(&self) -> impl Iterator> { - self.end_entity_cert_ders.iter() - } - /// Return `true` if the EKU OID is allowed. - pub fn has_allowed_eku<'a>(&self, eku: &'a ExtendedKeyUsage) -> Option> { + pub(crate) fn has_allowed_eku<'a>(&self, eku: &'a ExtendedKeyUsage) -> Option> { if eku.email_protection { return Some(EMAIL_PROTECTION_OID.clone()); } @@ -242,8 +317,79 @@ impl CertificateAcceptancePolicy { } } +fn base64_sha256_cert_der(cert_der: &[u8]) -> String { + let cert_sha256 = sha256(cert_der); + base64::encode(&cert_sha256) +} + +/// Describes errors that can be identified when evaluating a certificate's +/// trust. +#[derive(Debug, Eq, Error, PartialEq)] +#[non_exhaustive] +pub enum CertificateTrustError { + /// The certificate does not appear on any trust list that has been + /// configured. + /// + /// A certificate can be approved either by adding one or more trust anchors + /// via a call to [`CertificateTrustPolicy::add_trust_anchors`] or by + /// adding one or more end-entity certificates via + /// [`CertificateTrustPolicy::add_end_entity_credentials`]. + /// + /// If the certificate that was presented doesn't match either of these + /// conditions, this error will be returned. + #[error("the certificate is not trusted")] + CertificateNotTrusted, + + /// The certificate contains an invalid extended key usage (EKU) value. + #[error("the certificate contains an invalid extended key usage (EKU) value")] + InvalidEku, + + /// An error was reported by the OpenSSL native code. + /// + /// NOTE: We do not directly capture the OpenSSL error itself because it + /// lacks an Eq implementation. Instead we capture the error description. + #[cfg(feature = "openssl")] + #[error("an error was reported by OpenSSL native code: {0}")] + OpenSslError(String), + + /// The OpenSSL native code mutex could not be acquired. + #[cfg(feature = "openssl")] + #[error(transparent)] + OpenSslMutexUnavailable(#[from] crate::openssl::OpenSslMutexUnavailable), + + /// The certificate (or certificate chain) that was presented is invalid. + #[error("the certificate or certificate chain is invalid")] + InvalidCertificate, + + /// An unexpected internal error occured while requesting the time stamp + /// response. + #[error("internal error ({0})")] + InternalError(&'static str), +} + +#[cfg(feature = "openssl")] +impl From for CertificateTrustError { + fn from(err: openssl::error::ErrorStack) -> Self { + Self::OpenSslError(err.to_string()) + } +} + +#[cfg(target_arch = "wasm32")] +impl From for CertificateTrustError { + fn from(err: crate::webcrypto::WasmCryptoError) -> Self { + match err { + crate::webcrypto::WasmCryptoError::UnknownContext => { + Self::InternalError("unknown WASM context") + } + crate::webcrypto::WasmCryptoError::NoCryptoAvailable => { + Self::InternalError("WASM crypto unavailable") + } + } + } +} + /// This error can occur when adding certificates to a -/// [`CertificateAcceptancePolicy`]. +/// [`CertificateTrustPolicy`]. #[derive(Debug, Eq, PartialEq)] pub struct InvalidCertificateError(pub(crate) String); diff --git a/internal/crypto/src/cose/mod.rs b/internal/crypto/src/cose/mod.rs index 72a767bdd..0947f90a0 100644 --- a/internal/crypto/src/cose/mod.rs +++ b/internal/crypto/src/cose/mod.rs @@ -15,8 +15,13 @@ //! //! [COSE]: https://datatracker.ietf.org/doc/rfc9052/ -mod certificate_acceptance_policy; -pub use certificate_acceptance_policy::{CertificateAcceptancePolicy, InvalidCertificateError}; +mod certificate_trust_policy; +pub use certificate_trust_policy::{ + CertificateTrustError, CertificateTrustPolicy, InvalidCertificateError, +}; + +mod certificate_profile; +pub use certificate_profile::{check_certificate_profile, CertificateProfileError}; mod error; pub use error::CoseError; diff --git a/internal/crypto/src/openssl/check_certificate_trust.rs b/internal/crypto/src/openssl/check_certificate_trust.rs new file mode 100644 index 000000000..127750f12 --- /dev/null +++ b/internal/crypto/src/openssl/check_certificate_trust.rs @@ -0,0 +1,71 @@ +// Copyright 2022 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use openssl::{ + stack::Stack, + x509::{verify::X509VerifyFlags, X509StoreContext, X509}, +}; + +use crate::{ + cose::{CertificateTrustError, CertificateTrustPolicy}, + openssl::OpenSslMutex, +}; + +pub(crate) fn check_certificate_trust( + ctp: &CertificateTrustPolicy, + chain_der: &[Vec], + cert_der: &[u8], + signing_time_epoch: Option, +) -> Result<(), CertificateTrustError> { + let _openssl = OpenSslMutex::acquire()?; + + let mut cert_chain = Stack::new()?; + for cert_der in chain_der { + let x509_cert = X509::from_der(cert_der)?; + cert_chain.push(x509_cert)?; + } + + let cert = X509::from_der(cert_der)?; + + let mut builder = openssl::x509::store::X509StoreBuilder::new()?; + let mut verify_param = openssl::x509::verify::X509VerifyParam::new()?; + + if let Some(st) = signing_time_epoch { + verify_param.set_time(st); + } else { + verify_param.set_flags(X509VerifyFlags::NO_CHECK_TIME)?; + } + + builder.set_param(&verify_param)?; + + // Add trust anchors. + let mut has_anchors = false; + for der in ctp.trust_anchor_ders() { + let root_cert = X509::from_der(der)?; + builder.add_cert(root_cert)?; + has_anchors = true; + } + + let store = builder.build(); + + if !has_anchors { + return Err(CertificateTrustError::CertificateNotTrusted); + } + + let mut store_ctx = X509StoreContext::new()?; + if store_ctx.init(&store, cert.as_ref(), &cert_chain, |f| f.verify_cert())? { + Ok(()) + } else { + Err(CertificateTrustError::CertificateNotTrusted) + } +} diff --git a/internal/crypto/src/openssl/mod.rs b/internal/crypto/src/openssl/mod.rs index 78fda28d4..7cbff7d7a 100644 --- a/internal/crypto/src/openssl/mod.rs +++ b/internal/crypto/src/openssl/mod.rs @@ -23,5 +23,6 @@ mod cert_chain; mod ffi_mutex; pub use ffi_mutex::{OpenSslMutex, OpenSslMutexUnavailable}; +pub(crate) mod check_certificate_trust; pub(crate) mod signers; pub mod validators; diff --git a/internal/crypto/src/p1363.rs b/internal/crypto/src/p1363.rs index 838a4d80b..ef70cb4eb 100644 --- a/internal/crypto/src/p1363.rs +++ b/internal/crypto/src/p1363.rs @@ -44,10 +44,8 @@ pub struct EcSigComps<'a> { pub s: &'a [u8], } -#[cfg(feature = "openssl")] use crate::{raw_signature::RawSignerError, SigningAlg}; -#[cfg(feature = "openssl")] pub(crate) fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Result, RawSignerError> { // P1363 format: r | s diff --git a/internal/crypto/src/raw_signature/signer.rs b/internal/crypto/src/raw_signature/signer.rs index cca4f607d..24dab81b0 100644 --- a/internal/crypto/src/raw_signature/signer.rs +++ b/internal/crypto/src/raw_signature/signer.rs @@ -267,3 +267,40 @@ impl AsyncTimeStampProvider for AsyncRawSignerWrapper { self.0.send_time_stamp_request(message) } } + +#[cfg(test)] +#[allow(clippy::unwrap_used)] +pub(crate) fn test_signer(alg: SigningAlg) -> Box { + let (cert_chain, private_key) = match alg { + SigningAlg::Ed25519 => ( + include_bytes!("../tests/fixtures/raw_signature/ed25519.pub").as_slice(), + include_bytes!("../tests/fixtures/raw_signature/ed25519.priv").as_slice(), + ), + SigningAlg::Es256 => ( + include_bytes!("../tests/fixtures/raw_signature/es256.pub").as_slice(), + include_bytes!("../tests/fixtures/raw_signature/es256.priv").as_slice(), + ), + SigningAlg::Es384 => ( + include_bytes!("../tests/fixtures/raw_signature/es384.pub").as_slice(), + include_bytes!("../tests/fixtures/raw_signature/es384.priv").as_slice(), + ), + SigningAlg::Es512 => ( + include_bytes!("../tests/fixtures/raw_signature/es512.pub").as_slice(), + include_bytes!("../tests/fixtures/raw_signature/es512.priv").as_slice(), + ), + SigningAlg::Ps256 => ( + include_bytes!("../tests/fixtures/raw_signature/ps256.pub").as_slice(), + include_bytes!("../tests/fixtures/raw_signature/ps256.priv").as_slice(), + ), + SigningAlg::Ps384 => ( + include_bytes!("../tests/fixtures/raw_signature/ps384.pub").as_slice(), + include_bytes!("../tests/fixtures/raw_signature/ps384.priv").as_slice(), + ), + SigningAlg::Ps512 => ( + include_bytes!("../tests/fixtures/raw_signature/ps512.pub").as_slice(), + include_bytes!("../tests/fixtures/raw_signature/ps512.priv").as_slice(), + ), + }; + + signer_from_cert_chain_and_private_key(cert_chain, private_key, alg, None).unwrap() +} diff --git a/internal/crypto/src/tests/cose/certificate_acceptance_policy.rs b/internal/crypto/src/tests/cose/certificate_acceptance_policy.rs deleted file mode 100644 index 20f2ac225..000000000 --- a/internal/crypto/src/tests/cose/certificate_acceptance_policy.rs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2024 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -use asn1_rs::{oid, Oid}; -#[cfg(target_arch = "wasm32")] -use wasm_bindgen_test::wasm_bindgen_test; -use x509_parser::prelude::ExtendedKeyUsage; - -use crate::cose::{CertificateAcceptancePolicy, InvalidCertificateError}; - -#[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn impl_debug() { - let cap = CertificateAcceptancePolicy::new(); - let _ = format!("{cap:#?}"); -} - -#[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn new() { - let cap = CertificateAcceptancePolicy::new(); - - assert_eq!( - cap.has_allowed_eku(&email_eku()).unwrap(), - EMAIL_PROTECTION_OID - ); - - assert!(cap.has_allowed_eku(&document_signing_eku()).is_none()); - - assert_eq!( - cap.has_allowed_eku(&time_stamping_eku()).unwrap(), - TIME_STAMPING_OID - ); - - assert_eq!( - cap.has_allowed_eku(&ocsp_signing_eku()).unwrap(), - OCSP_SIGNING_OID - ); -} - -#[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn default() { - let cap = CertificateAcceptancePolicy::default(); - - assert_eq!( - cap.has_allowed_eku(&email_eku()).unwrap(), - EMAIL_PROTECTION_OID - ); - - assert_eq!( - cap.has_allowed_eku(&document_signing_eku()).unwrap(), - DOCUMENT_SIGNING_OID - ); - - assert_eq!( - cap.has_allowed_eku(&time_stamping_eku()).unwrap(), - TIME_STAMPING_OID - ); - - assert_eq!( - cap.has_allowed_eku(&ocsp_signing_eku()).unwrap(), - OCSP_SIGNING_OID - ); -} - -#[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn clear() { - let mut cap = CertificateAcceptancePolicy::default(); - cap.clear(); - - assert_eq!( - cap.has_allowed_eku(&email_eku()).unwrap(), - EMAIL_PROTECTION_OID - ); - - assert!(cap.has_allowed_eku(&document_signing_eku()).is_none()); - - assert_eq!( - cap.has_allowed_eku(&time_stamping_eku()).unwrap(), - TIME_STAMPING_OID - ); - - assert_eq!( - cap.has_allowed_eku(&ocsp_signing_eku()).unwrap(), - OCSP_SIGNING_OID - ); -} - -#[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn add_valid_ekus_err_bad_utf8() { - let mut cap = CertificateAcceptancePolicy::new(); - cap.add_valid_ekus(&[128, 0]); - - assert_eq!( - cap.has_allowed_eku(&email_eku()).unwrap(), - EMAIL_PROTECTION_OID - ); - - assert!(cap.has_allowed_eku(&document_signing_eku()).is_none()); - - assert_eq!( - cap.has_allowed_eku(&time_stamping_eku()).unwrap(), - TIME_STAMPING_OID - ); - - assert_eq!( - cap.has_allowed_eku(&ocsp_signing_eku()).unwrap(), - OCSP_SIGNING_OID - ); -} - -#[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn add_trust_anchors_err_bad_pem() { - let mut cap = CertificateAcceptancePolicy::new(); - assert!(cap.add_trust_anchors(BAD_PEM.as_bytes()).is_err()); -} - -#[test] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn add_end_entity_credentials_err_bad_pem() { - let mut cap = CertificateAcceptancePolicy::new(); - assert!(cap.add_end_entity_credentials(BAD_PEM.as_bytes()).is_err()); -} - -#[test] -#[cfg_attr(target_arch = "wasm32", 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(target_arch = "wasm32", 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----- -"#; diff --git a/internal/crypto/src/tests/cose/certificate_profile.rs b/internal/crypto/src/tests/cose/certificate_profile.rs new file mode 100644 index 000000000..9af841ad8 --- /dev/null +++ b/internal/crypto/src/tests/cose/certificate_profile.rs @@ -0,0 +1,65 @@ +// Copyright 2022 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use c2pa_status_tracker::{ + validation_codes::SIGNING_CREDENTIAL_EXPIRED, DetailedStatusTracker, StatusTracker, +}; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::wasm_bindgen_test; +use x509_parser::pem::Pem; + +use crate::cose::{check_certificate_profile, CertificateTrustPolicy}; + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn expired_cert() { + let ctp = CertificateTrustPolicy::default(); + let mut validation_log = DetailedStatusTracker::default(); + + let cert_der = x509_der_from_pem(include_bytes!( + "../fixtures/cose/rsa-pss256_key-expired.pub" + )); + + assert!(check_certificate_profile(&cert_der, &ctp, &mut validation_log, None).is_err()); + + assert!(!validation_log.logged_items().is_empty()); + + assert_eq!( + validation_log.logged_items()[0].validation_status, + Some(SIGNING_CREDENTIAL_EXPIRED.into()) + ); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cert_algorithms() { + let ctp = CertificateTrustPolicy::default(); + + let mut validation_log = DetailedStatusTracker::default(); + + let es256_cert = x509_der_from_pem(include_bytes!("../fixtures/raw_signature/es256.pub")); + let es384_cert = x509_der_from_pem(include_bytes!("../fixtures/raw_signature/es384.pub")); + let es512_cert = x509_der_from_pem(include_bytes!("../fixtures/raw_signature/es512.pub")); + let ps256_cert = x509_der_from_pem(include_bytes!("../fixtures/raw_signature/ps256.pub")); + + check_certificate_profile(&es256_cert, &ctp, &mut validation_log, None).unwrap(); + check_certificate_profile(&es384_cert, &ctp, &mut validation_log, None).unwrap(); + check_certificate_profile(&es512_cert, &ctp, &mut validation_log, None).unwrap(); + check_certificate_profile(&ps256_cert, &ctp, &mut validation_log, None).unwrap(); +} + +fn x509_der_from_pem(cert_pem: &[u8]) -> Vec { + let mut pems = Pem::iter_from_buffer(cert_pem); + let pem = pems.next().unwrap().unwrap(); + pem.contents +} diff --git a/internal/crypto/src/tests/cose/certificate_trust_policy.rs b/internal/crypto/src/tests/cose/certificate_trust_policy.rs new file mode 100644 index 000000000..243225b8f --- /dev/null +++ b/internal/crypto/src/tests/cose/certificate_trust_policy.rs @@ -0,0 +1,620 @@ +// Copyright 2024 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use asn1_rs::{oid, Oid}; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::wasm_bindgen_test; +use x509_parser::{extensions::ExtendedKeyUsage, pem::Pem}; + +use crate::{ + cose::{CertificateTrustError, CertificateTrustPolicy, InvalidCertificateError}, + raw_signature::signer::test_signer, + SigningAlg, +}; + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn impl_debug() { + let ctp = CertificateTrustPolicy::new(); + let _ = format!("{ctp:#?}"); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", 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(target_arch = "wasm32", 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(target_arch = "wasm32", 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(target_arch = "wasm32", 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(target_arch = "wasm32", wasm_bindgen_test)] +fn add_trust_anchors_err_bad_pem() { + let mut ctp = CertificateTrustPolicy::new(); + assert!(ctp.add_trust_anchors(BAD_PEM.as_bytes()).is_err()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", 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(target_arch = "wasm32", 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(target_arch = "wasm32", 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_trust_store() { + let ctp = CertificateTrustPolicy::default(); + + let ps256 = test_signer(SigningAlg::Ps256); + let ps384 = test_signer(SigningAlg::Ps384); + let ps512 = test_signer(SigningAlg::Ps512); + let es256 = test_signer(SigningAlg::Es256); + let es384 = test_signer(SigningAlg::Es384); + let es512 = test_signer(SigningAlg::Es512); + let ed25519 = test_signer(SigningAlg::Ed25519); + + let ps256_certs = ps256.cert_chain().unwrap(); + let ps384_certs = ps384.cert_chain().unwrap(); + let ps512_certs = ps512.cert_chain().unwrap(); + let es256_certs = es256.cert_chain().unwrap(); + let es384_certs = es384.cert_chain().unwrap(); + let es512_certs = es512.cert_chain().unwrap(); + let ed25519_certs = ed25519.cert_chain().unwrap(); + + 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(); +} + +#[cfg_attr(not(target_arch = "wasm32"), actix::test)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +async fn test_trust_store_async() { + let ctp = CertificateTrustPolicy::default(); + + let ps256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps256.pub")); + let ps384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps384.pub")); + let ps512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps512.pub")); + let es256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es256.pub")); + let es384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es384.pub")); + let es512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es512.pub")); + let ed25519_certs = cert_ders_from_pem(include_bytes!("../fixtures/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_signer(SigningAlg::Ps256); + let ps384 = test_signer(SigningAlg::Ps384); + let ps512 = test_signer(SigningAlg::Ps512); + let es256 = test_signer(SigningAlg::Es256); + let es384 = test_signer(SigningAlg::Es384); + let es512 = test_signer(SigningAlg::Es512); + let ed25519 = test_signer(SigningAlg::Ed25519); + + let ps256_certs = ps256.cert_chain().unwrap(); + let ps384_certs = ps384.cert_chain().unwrap(); + let ps512_certs = ps512.cert_chain().unwrap(); + let es256_certs = es256.cert_chain().unwrap(); + let es384_certs = es384.cert_chain().unwrap(); + let es512_certs = es512.cert_chain().unwrap(); + let ed25519_certs = ed25519.cert_chain().unwrap(); + + // Break the trust chain by skipping the first intermediate CA. + 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 + ); +} + +#[cfg_attr(not(target_arch = "wasm32"), actix::test)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +async fn test_broken_trust_chain_async() { + let ctp = CertificateTrustPolicy::default(); + + let ps256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps256.pub")); + let ps384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps384.pub")); + let ps512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps512.pub")); + let es256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es256.pub")); + let es384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es384.pub")); + let es512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es512.pub")); + let ed25519_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ed25519.pub")); + + // Break the trust chain by skipping the first intermediate CA. + 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!("../fixtures/raw_signature/ed25519.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/es256.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/es384.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/es512.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/ps256.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/ps384.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/ps512.pub")) + .unwrap(); + + let ps256 = test_signer(SigningAlg::Ps256); + let ps384 = test_signer(SigningAlg::Ps384); + let ps512 = test_signer(SigningAlg::Ps512); + let es256 = test_signer(SigningAlg::Es256); + let es384 = test_signer(SigningAlg::Es384); + let es512 = test_signer(SigningAlg::Es512); + let ed25519 = test_signer(SigningAlg::Ed25519); + + let ps256_certs = ps256.cert_chain().unwrap(); + let ps384_certs = ps384.cert_chain().unwrap(); + let ps512_certs = ps512.cert_chain().unwrap(); + let es256_certs = es256.cert_chain().unwrap(); + let es384_certs = es384.cert_chain().unwrap(); + let es512_certs = es512.cert_chain().unwrap(); + let ed25519_certs = ed25519.cert_chain().unwrap(); + + 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(); +} + +#[cfg_attr(not(target_arch = "wasm32"), actix::test)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +async fn test_allowed_list_async() { + let mut ctp = CertificateTrustPolicy::new(); + + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/ed25519.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/es256.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/es384.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/es512.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/ps256.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/ps384.pub")) + .unwrap(); + ctp.add_end_entity_credentials(include_bytes!("../fixtures/raw_signature/ps512.pub")) + .unwrap(); + + let ps256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps256.pub")); + let ps384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps384.pub")); + let ps512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps512.pub")); + let es256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es256.pub")); + let es384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es384.pub")); + let es512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es512.pub")); + let ed25519_certs = cert_ders_from_pem(include_bytes!("../fixtures/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!( + "../fixtures/raw_signature/allowed_list.hash" + )) + .unwrap(); + + let ps256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps256.pub")); + let ps384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps384.pub")); + let ps512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps512.pub")); + let es256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es256.pub")); + let es384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es384.pub")); + let es512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es512.pub")); + let ed25519_certs = cert_ders_from_pem(include_bytes!("../fixtures/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(); +} + +#[cfg_attr(not(target_arch = "wasm32"), actix::test)] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +async fn test_allowed_list_hashes_async() { + let mut ctp = CertificateTrustPolicy::new(); + + ctp.add_end_entity_credentials(include_bytes!( + "../fixtures/raw_signature/allowed_list.hash" + )) + .unwrap(); + + let ps256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps256.pub")); + let ps384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps384.pub")); + let ps512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/ps512.pub")); + let es256_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es256.pub")); + let es384_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es384.pub")); + let es512_certs = cert_ders_from_pem(include_bytes!("../fixtures/raw_signature/es512.pub")); + let ed25519_certs = cert_ders_from_pem(include_bytes!("../fixtures/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> { + Pem::iter_from_buffer(cert_chain) + .map(|r| r.unwrap().contents) + .collect() +} diff --git a/internal/crypto/src/tests/cose/mod.rs b/internal/crypto/src/tests/cose/mod.rs index f7c21ff16..3c4f5adf3 100644 --- a/internal/crypto/src/tests/cose/mod.rs +++ b/internal/crypto/src/tests/cose/mod.rs @@ -11,4 +11,5 @@ // specific language governing permissions and limitations under // each license. -mod certificate_acceptance_policy; +mod certificate_profile; +mod certificate_trust_policy; diff --git a/internal/crypto/src/tests/fixtures/cose/rsa-pss256_key-expired.pub b/internal/crypto/src/tests/fixtures/cose/rsa-pss256_key-expired.pub new file mode 100644 index 000000000..b091ae605 --- /dev/null +++ b/internal/crypto/src/tests/fixtures/cose/rsa-pss256_key-expired.pub @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEATCCAumgAwIBAgIUAMXK1m2iDtQ165Ruxogp1TjplcUwDQYJKoZIhvcNAQEL +BQAwfzELMAkGA1UEBhMCdXMxCzAJBgNVBAgMAmNhMREwDwYDVQQHDAhTYW4gSm9z +ZTEUMBIGA1UECgwLQWRvYmUsIEluYy4xGDAWBgNVBAsMD0NBSSAoVGVtcG9yYXJ5 +KTEgMB4GA1UEAwwXY29udGVudGF1dGhlbnRpY2l0eS5vcmcwHhcNMjIwMjAyMTMy +NDI4WhcNMjIwMjAzMTMyNDI4WjB/MQswCQYDVQQGEwJ1czELMAkGA1UECAwCY2Ex +ETAPBgNVBAcMCFNhbiBKb3NlMRQwEgYDVQQKDAtBZG9iZSwgSW5jLjEYMBYGA1UE +CwwPQ0FJIChUZW1wb3JhcnkpMSAwHgYDVQQDDBdjb250ZW50YXV0aGVudGljaXR5 +Lm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAML8WVHo1HcXkQ7g +P73aP+9AfZE8uNq0sFIHB9jNF9HN5UYCPLfYr4v6XJd1I1wkygypxZgBsI5UUfKY +uMDhijf5+Obxv52Y+4Ein59oKUuM6B53aaiCvwRmTHp2MeOqt5nTkhvCarhdJipn +tn+zNLzGGAdXu0jmS/h4dH0nPN0qJdfliDtkPSRvTqAEfOAPfGas/lTWX/8baVg6 +cOMnLupYR0787WE4Y6n4ynRLgwsMj8K2GCIHFceHQMhE8lMjC6ltmmR8dbnCOxMM +QDB8SZLyMEKHWw8jM6fz5RVW/L7B0kcww/tPqE4ODBU1k6SZIti8DT7ONpgaufzH +J5vjmEMCAwEAAaN1MHMwHQYDVR0OBBYEFJNnwVuf7Gh/xPkAvGGmK+Q6gB3NMB8G +A1UdIwQYMBaAFJNnwVuf7Gh/xPkAvGGmK+Q6gB3NMA8GA1UdEwEB/wQFMAMBAf8w +CwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMEMA0GCSqGSIb3DQEBCwUA +A4IBAQAVBKqok5V5SxnfAE5Mt9WyQjfMWu19MwrquPo0I40KyYJI+2coLyo2tkuE +bXezJTP/RY7v2hqOwlk+2kK82m2idRZcRT3r8yAUwKkg/tX5jlVt4WTNQgdamVtd +mpCb97ozXdjGGv/FlSji5ufs7u8hNP9jhO0/LFzHoeJDRJc13HBJCdOmGd0topWZ +NW4UtOdmke5ZPxaWkjxDwtfJxsplsinD3bcaiYGfhQPSNyacYB0UnimdUQaVpO6i +U9t08t4+zBoFpCoC9XgEAOs9DP7WOGmKPhafL7dbSUhqyFHwacGY3PxoWZPqHwpH +jsseAnInq8Ves5Bhnd6l6sc4GjiR +-----END CERTIFICATE----- diff --git a/sdk/tests/fixtures/certs/trust/allowed_list.hash b/internal/crypto/src/tests/fixtures/raw_signature/allowed_list.hash similarity index 100% rename from sdk/tests/fixtures/certs/trust/allowed_list.hash rename to internal/crypto/src/tests/fixtures/raw_signature/allowed_list.hash diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps256.priv b/internal/crypto/src/tests/fixtures/raw_signature/ps256.priv index 877712f05..ed4c7318d 100644 --- a/internal/crypto/src/tests/fixtures/raw_signature/ps256.priv +++ b/internal/crypto/src/tests/fixtures/raw_signature/ps256.priv @@ -1,52 +1,53 @@ -----BEGIN PRIVATE KEY----- -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 -Ojrhn4um0bWD0vEStufjDsDov/TL0Bo= +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 +G/dZR0Qj3j0T5F9GX719XpmzYbVFKIKPTsKF -----END PRIVATE KEY----- diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps256.pub b/internal/crypto/src/tests/fixtures/raw_signature/ps256.pub index 47929fc50..9ed670a94 100644 --- a/internal/crypto/src/tests/fixtures/raw_signature/ps256.pub +++ b/internal/crypto/src/tests/fixtures/raw_signature/ps256.pub @@ -1,36 +1,76 @@ -----BEGIN CERTIFICATE----- -MIIGVzCCBAugAwIBAgIUBCFBOCIjNtCU37ACTfi+QfTkI7QwQQYJKoZIhvcNAQEK +MIIGsDCCBGSgAwIBAgIUfj5imtzP59mXEBNbWkgFaXLfgZkwQQYJKoZIhvcNAQEK MDSgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEF -AKIDAgEgMHYxCzAJBgNVBAYTAnVzMQswCQYDVQQIDAJjYTESMBAGA1UEBwwJU29t -ZXdoZXJlMRUwEwYDVQQKDAxTb21lIENvbXBhbnkxGTAXBgNVBAsMEEZPUiBURVNU -SU5HIE9OTFkxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTI0MTExODE5MDExMloX -DTI1MDUxNzE5MDExMlowdjELMAkGA1UEBhMCdXMxCzAJBgNVBAgMAmNhMRIwEAYD -VQQHDAlTb21ld2hlcmUxFTATBgNVBAoMDFNvbWUgQ29tcGFueTEZMBcGA1UECwwQ -Rk9SIFRFU1RJTkcgT05MWTEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQC57PRjgHzQU0yvx6Gkmzg9WDVYzFESquAm -3c6QCfKjnvD62JTxjxlg/U/f+6+mCbb+XJA4P0KRrQ5wlB77j6SfcRDxIEsDtlEx -NM/UXwaNC1ZHG+TFHDFaHj6jpJLczAgSgmnUo4vHHTjO78SEQS7HXiQbPJnkEEaF -UocRQ9pAAylbDsa9Z8o7v8lCImwkoSGW2OhMN4IqOrvOZqMPJsUE/5jJ/aGuxO8e -F/PowzJnhV3fkBDXZ2079Sc6+NAJWt2eyrc/Zt4kooY9U4yEUgPrCu8oDgcSipcd -ZrS7cEuLVYZtqkcoloZicmPCTNlOmmaQP0cbbxJeWuOe7BNa9qV/Gtso/GU9o1pA -LumBuS8TSGpwgwS7X9gFFCyrjXINqtlpHjxbZ5JDg2N5EO9gDrG858CGOplOj8dz -wpPIWZ4rKUuDzY7e8yLHyQ4GR9cWhtB39P39fmOrh32oKKaU6hAimYjg9MM+MQEE -v56OflUufH7Xzj5YrJsyeks4s7GkQV+7GiWzl4e9Zc5nEVmggx6GRt9b+PWLV2px -HTMcBjIY2FycYuxjxi7tXjLNeWzdPEqKUdraWZfNQFip/LBuwqOU1TnnhOq2Dhl+ -fXmAekKo0OtW8XcCwK3xvpZ9n17Qng1gCbQnz6qMEyV35lyQgExyT7sFy3yYVuur -4FXH29iSDwIDAQABo3UwczAdBgNVHQ4EFgQUuT2YsFLnLlua8GziUBIMgpIjg1Iw -HwYDVR0jBBgwFoAUuT2YsFLnLlua8GziUBIMgpIjg1IwDwYDVR0TAQH/BAUwAwEB -/zALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwQwQQYJKoZIhvcNAQEK +AKIDAgEgMIGMMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNv +bWV3aGVyZTEnMCUGA1UECgweQzJQQSBUZXN0IEludGVybWVkaWF0ZSBSb290IENB +MRkwFwYDVQQLDBBGT1IgVEVTVElOR19PTkxZMRgwFgYDVQQDDA9JbnRlcm1lZGlh +dGUgQ0EwHhcNMjIwNjEwMTg0NjI4WhcNMzAwODI2MTg0NjI4WjCBgDELMAkGA1UE +BhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUxHzAdBgNVBAoM +FkMyUEEgVGVzdCBTaWduaW5nIENlcnQxGTAXBgNVBAsMEEZPUiBURVNUSU5HX09O +TFkxFDASBgNVBAMMC0MyUEEgU2lnbmVyMIICVjBBBgkqhkiG9w0BAQowNKAPMA0G +CWCGSAFlAwQCAQUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASAD +ggIPADCCAgoCggIBAOtiNSWBpKkHL78khDYV2HTYkVUmTu5dgn20GiUjOjWhAyWK +5uZL+iuHWmHUOq0xqC39R+hyaMkcIAUf/XcJRK40Jh1s2kJ4+kCk7+RB1n1xeZeJ +jrKhJ7zCDhH6eFVqO9Om3phcpZyKt01yDkhfIP95GzCILuPm5lLKYI3P0FmpC8zl +5ctevgG1TXJcX8bNU6fsHmmw0rBrVXUOR+N1MOFO/h++mxIhhLW601XrgYu6lDQD +IDOc/IxwzEp8+SAzL3v6NStBEYIq2d+alUgEUAOM8EzZsungs0dovMPGcfw7COsG +4xrdmLHExRau4E1g1ANfh2QsYdraNMtS/wcpI1PG6BkqUQ4zlMoO/CI2nZ5oninb +uL9x/UJt+a6VvHA0e4bTIcJJVq3/t69mpZtNe6WqDfGU+KLZ5HJSBNSW9KyWxSAU +FuDFAMtKZRZmTBonKHSjYlYtT+/WN7n/LgFJ2EYxPeFcGGPrVqRTw38g0QA8cyFe +wHfQBZUiSKdvMRB1zmIj+9nmYsh8ganJzuPaUgsGNVKoOJZHq+Ya3ewBjwslR91k +QtEGq43PRCvx4Vf+qiXeMCzK+L1Gg0v+jt80grz+y8Ch5/EkxitaH/ei/HRJGyvD +Zu7vrV6fbWLfWysBoFStHWirQcocYDGsFm9hh7bwM+W0qvNB/hbRQ0xfrMI9AgMB +AAGjeDB2MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwQwDgYD +VR0PAQH/BAQDAgbAMB0GA1UdDgQWBBQ3KHUtnyxDJcV9ncAu37sql3aF7jAfBgNV +HSMEGDAWgBQMMoDK5ZZtTx/7+QsB1qnlDNwA4jBBBgkqhkiG9w0BAQowNKAPMA0G +CWCGSAFlAwQCAQUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASAD +ggIBAAmBZubOjnCXIYmg2l1pDYH+XIyp5feayZz6Nhgz6xB7CouNgvcjkYW7EaqN +RuEkAJWJC68OnjMwwe6tXWQC4ifMKbVg8aj/IRaVAqkEL/MRQ89LnL9F9AGxeugJ +ulYtpqzFOJUKCPxcXGEoPyqjY7uMdTS14JzluKUwtiQZAm4tcwh/ZdRkt69i3wRq +VxIY2TK0ncvr4N9cX1ylO6m+GxufseFSO0NwEMxjonJcvsxFwjB8eFUhE0yH3pdD +gqE2zYfv9kjYkFGngtOqbCe2ixRM5oj9qoS+aKVdOi9m/gObcJkSW9JYAJD2GHLO +yLpGWRhg4xnn1s7n2W9pWB7+txNR7aqkrUNhZQdznNVdWRGOale4uHJRSPZAetQT +oYoVAyIX1ba1L/GRo52mOOT67AJhmIVVJJFVvMvvJeQ8ktW8GlxYjG9HHbRpE0S1 +Hv7FhOg0vEAqyrKcYn5JWYGAvEr0VqUqBPz3/QZ8gbmJwXinnUku1QZbGZUIFFIS +3MDaPXMWmp2KuNMxJXHE1CfaiD7yn2plMV5QZakde3+Kfo6qv2GISK+WYhnGZAY/ +LxtEOqwVrQpDQVJ5jgR/RKPIsOobdboR/aTVjlp7OOfvLxFUvD66zOiVa96fAsfw +ltU2Cp0uWdQKSLoktmQWLYgEe3QOqvgLDeYP2ScAdm+S+lHV +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +wvq03sA= -----END CERTIFICATE----- diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps256.pub_key b/internal/crypto/src/tests/fixtures/raw_signature/ps256.pub_key index abd47a72d..ec6b57a5e 100644 Binary files a/internal/crypto/src/tests/fixtures/raw_signature/ps256.pub_key and b/internal/crypto/src/tests/fixtures/raw_signature/ps256.pub_key differ diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps256.raw_sig b/internal/crypto/src/tests/fixtures/raw_signature/ps256.raw_sig index 34900b713..0a7b7d660 100644 Binary files a/internal/crypto/src/tests/fixtures/raw_signature/ps256.raw_sig and b/internal/crypto/src/tests/fixtures/raw_signature/ps256.raw_sig differ diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps384.priv b/internal/crypto/src/tests/fixtures/raw_signature/ps384.priv index 5fe7ca12b..0925372a9 100644 --- a/internal/crypto/src/tests/fixtures/raw_signature/ps384.priv +++ b/internal/crypto/src/tests/fixtures/raw_signature/ps384.priv @@ -1,52 +1,53 @@ -----BEGIN PRIVATE KEY----- -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 -EmeuakxUPnIA/j1PIkHudR4FDqHUUg== +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 +3tua228WMQ3Sc+Hm+GUn4kI+is5V1Yw= -----END PRIVATE KEY----- diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps384.pub b/internal/crypto/src/tests/fixtures/raw_signature/ps384.pub index 95d83001e..28b55864b 100644 --- a/internal/crypto/src/tests/fixtures/raw_signature/ps384.pub +++ b/internal/crypto/src/tests/fixtures/raw_signature/ps384.pub @@ -1,36 +1,77 @@ -----BEGIN CERTIFICATE----- -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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +LxWpjig= -----END CERTIFICATE----- + diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps384.pub_key b/internal/crypto/src/tests/fixtures/raw_signature/ps384.pub_key index 15d7441ca..38f5aec66 100644 Binary files a/internal/crypto/src/tests/fixtures/raw_signature/ps384.pub_key and b/internal/crypto/src/tests/fixtures/raw_signature/ps384.pub_key differ diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps384.raw_sig b/internal/crypto/src/tests/fixtures/raw_signature/ps384.raw_sig index e7790f89d..5b097bef1 100644 Binary files a/internal/crypto/src/tests/fixtures/raw_signature/ps384.raw_sig and b/internal/crypto/src/tests/fixtures/raw_signature/ps384.raw_sig differ diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps512.priv b/internal/crypto/src/tests/fixtures/raw_signature/ps512.priv index d6fbb6ef9..0e1fc5c84 100644 --- a/internal/crypto/src/tests/fixtures/raw_signature/ps512.priv +++ b/internal/crypto/src/tests/fixtures/raw_signature/ps512.priv @@ -1,52 +1,53 @@ -----BEGIN PRIVATE KEY----- -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 -rgQ9XXObL8vHzpW/dBMsd5lIiuL4Jw== +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 +pH2QDGsL0MKY4ZOue7YfmK+miF9PZ8cqsHUr -----END PRIVATE KEY----- diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps512.pub b/internal/crypto/src/tests/fixtures/raw_signature/ps512.pub index bde24e2f8..029c3e3c5 100644 --- a/internal/crypto/src/tests/fixtures/raw_signature/ps512.pub +++ b/internal/crypto/src/tests/fixtures/raw_signature/ps512.pub @@ -1,36 +1,76 @@ -----BEGIN CERTIFICATE----- -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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +c9oJ+uc= -----END CERTIFICATE----- diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps512.pub_key b/internal/crypto/src/tests/fixtures/raw_signature/ps512.pub_key index 35dedacb0..47287e10d 100644 Binary files a/internal/crypto/src/tests/fixtures/raw_signature/ps512.pub_key and b/internal/crypto/src/tests/fixtures/raw_signature/ps512.pub_key differ diff --git a/internal/crypto/src/tests/fixtures/raw_signature/ps512.raw_sig b/internal/crypto/src/tests/fixtures/raw_signature/ps512.raw_sig index 004875a42..7f18889a1 100644 Binary files a/internal/crypto/src/tests/fixtures/raw_signature/ps512.raw_sig and b/internal/crypto/src/tests/fixtures/raw_signature/ps512.raw_sig differ diff --git a/internal/crypto/src/webcrypto/check_certificate_trust.rs b/internal/crypto/src/webcrypto/check_certificate_trust.rs new file mode 100644 index 000000000..c29b52581 --- /dev/null +++ b/internal/crypto/src/webcrypto/check_certificate_trust.rs @@ -0,0 +1,282 @@ +// Copyright 2022 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use asn1_rs::{Any, Class, FromDer, Header, Tag}; +use nom::AsBytes; +use x509_parser::{ + certificate::X509Certificate, der_parser::oid, oid_registry::Oid, x509::AlgorithmIdentifier, +}; + +use crate::{ + cose::{CertificateTrustError, CertificateTrustPolicy}, + p1363::der_to_p1363, + raw_signature::RawSignatureValidationError, + webcrypto::async_validator_for_signing_alg, + SigningAlg, +}; + +pub(crate) async fn check_certificate_trust( + ctp: &CertificateTrustPolicy, + chain_der: &[Vec], + cert_der: &[u8], + _signing_time_epoch: Option, +) -> Result<(), CertificateTrustError> { + let Ok((_rem, cert)) = X509Certificate::from_der(cert_der) else { + return Err(CertificateTrustError::InvalidCertificate); + }; + + let Ok(Some(eku)) = cert.extended_key_usage() else { + return Err(CertificateTrustError::InvalidEku); + }; + + let Some(_approved_oid) = ctp.has_allowed_eku(&eku.value) else { + return Err(CertificateTrustError::InvalidEku); + }; + + // Add end-entity cert to the chain if not already there. + let full_chain = if !chain_der.is_empty() && cert_der == &chain_der[0] { + chain_der.to_vec() + } else { + let mut full_chain: Vec> = Vec::new(); + full_chain.push(cert_der.to_vec()); + let mut in_chain = chain_der.to_vec(); + full_chain.append(&mut in_chain); + full_chain + }; + + // Make sure chain is in the correct order and valid. + check_chain_order(&full_chain).await?; + + // Build anchors and check against trust anchors. + let anchors = ctp + .trust_anchor_ders() + .map(|anchor_der| { + X509Certificate::from_der(anchor_der) + .map_err(|_e| CertificateTrustError::CertificateNotTrusted) + .map(|r| r.1) + }) + .collect::, CertificateTrustError>>()?; + + if anchors.is_empty() { + return Err(CertificateTrustError::CertificateNotTrusted); + } + + // Work back from last cert in chain against the trust anchors. + for cert in chain_der.iter().rev() { + let (_, chain_cert) = X509Certificate::from_der(cert) + .map_err(|_e| CertificateTrustError::CertificateNotTrusted)?; + + for anchor in ctp.trust_anchor_ders() { + let data = chain_cert.tbs_certificate.as_ref(); + let sig = chain_cert.signature_value.as_ref(); + + let sig_alg = cert_signing_alg(&chain_cert); + + let (_, anchor_cert) = X509Certificate::from_der(anchor) + .map_err(|_e| CertificateTrustError::CertificateNotTrusted)?; + + if chain_cert.issuer() == anchor_cert.subject() { + let result = + verify_data(anchor.clone(), sig_alg, sig.to_vec(), data.to_vec()).await; + + match result { + Ok(b) => { + if b { + return Ok(()); + } + } + Err(_) => continue, + } + } + } + } + + // TO DO: Consider path check and names restrictions. + return Err(CertificateTrustError::CertificateNotTrusted); +} + +async fn check_chain_order(certs: &[Vec]) -> Result<(), CertificateTrustError> { + let chain_length = certs.len(); + if chain_length < 2 { + return Ok(()); + } + + for i in 1..chain_length { + let (_, current_cert) = X509Certificate::from_der(&certs[i - 1]) + .map_err(|_e| CertificateTrustError::CertificateNotTrusted)?; + + let issuer_der = certs[i].to_vec(); + let data = current_cert.tbs_certificate.as_ref(); + let sig = current_cert.signature_value.as_ref(); + let sig_alg = cert_signing_alg(¤t_cert); + + if !verify_data(issuer_der, sig_alg, sig.to_vec(), data.to_vec()).await? { + return Err(CertificateTrustError::CertificateNotTrusted); + } + } + + Ok(()) +} + +fn cert_signing_alg(cert: &X509Certificate) -> Option { + let cert_alg = &cert.signature_algorithm.algorithm; + + if *cert_alg == SHA256_WITH_RSAENCRYPTION_OID { + Some("rsa256".to_string()) + } else if *cert_alg == SHA384_WITH_RSAENCRYPTION_OID { + Some("rsa384".to_string()) + } else if *cert_alg == SHA512_WITH_RSAENCRYPTION_OID { + Some("rsa512".to_string()) + } else if *cert_alg == ECDSA_WITH_SHA256_OID { + Some(SigningAlg::Es256.to_string()) + } else if *cert_alg == ECDSA_WITH_SHA384_OID { + Some(SigningAlg::Es384.to_string()) + } else if *cert_alg == ECDSA_WITH_SHA512_OID { + Some(SigningAlg::Es512.to_string()) + } else if *cert_alg == RSASSA_PSS_OID { + signing_alg_from_rsapss_alg(&cert.signature_algorithm) + } else if *cert_alg == ED25519_OID { + Some(SigningAlg::Ed25519.to_string()) + } else { + None + } +} + +fn signing_alg_from_rsapss_alg(alg: &AlgorithmIdentifier) -> Option { + let Some(parameters) = &alg.parameters else { + return None; + }; + + let Ok(seq) = parameters.as_sequence() else { + return None; + }; + + let Ok((_i, (ha_alg, mgf_ai))) = seq.parse(|i| { + let (i, h) =
::from_der(i)?; + if h.class() != Class::ContextSpecific || h.tag() != Tag(0) { + return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); + } + + let (i, ha_alg) = AlgorithmIdentifier::from_der(i) + .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; + + let (i, h) =
::from_der(i)?; + if h.class() != Class::ContextSpecific || h.tag() != Tag(1) { + return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); + } + + let (i, mgf_ai) = AlgorithmIdentifier::from_der(i) + .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; + + // Ignore anything that follows these two parameters. + + Ok((i, (ha_alg, mgf_ai))) + }) else { + return None; + }; + + let Some(mgf_ai_parameters) = mgf_ai.parameters else { + return None; + }; + + let Ok(mgf_ai_parameters) = mgf_ai_parameters.as_sequence() else { + return None; + }; + + let Ok((_i, mgf_ai_params_algorithm)) = + ::from_der(&mgf_ai_parameters.content) + else { + return None; + }; + + let Ok(mgf_ai_params_algorithm) = mgf_ai_params_algorithm.as_oid() else { + return None; + }; + + // Algorithms must be the same. + if ha_alg.algorithm.to_id_string() != mgf_ai_params_algorithm.to_id_string() { + return None; + } + + // We only recognize a few specific algorithm types. + if ha_alg.algorithm == SHA256_OID { + Some("ps256".to_string()) + } else if ha_alg.algorithm == SHA384_OID { + Some("ps384".to_string()) + } else if ha_alg.algorithm == SHA512_OID { + Some("ps512".to_string()) + } else { + None + } +} + +async fn verify_data( + cert_der: Vec, + sig_alg: Option, + sig: Vec, + data: Vec, +) -> Result { + let (_, cert) = X509Certificate::from_der(cert_der.as_bytes()) + .map_err(|_e| CertificateTrustError::InvalidCertificate)?; + + let certificate_public_key = cert.public_key(); + + let Some(cert_alg_string) = sig_alg else { + return Err(CertificateTrustError::InvalidCertificate); + }; + + let signing_alg: SigningAlg = cert_alg_string + .parse() + .map_err(|_| CertificateTrustError::InvalidCertificate)?; + + // Not sure this is needed any more. Leaving this for now, but I think this + // should be handled in c2pa_crypto's raw signature code. + + // TO REVIEW: For now, this is needed because this function could validate C2PA + // signatures (P1363) or those from certificates which are ASN.1 DER. I don't + // know if the new code is only used for DER now. + let adjusted_sig = if cert_alg_string.starts_with("es") { + match der_to_p1363(&sig, signing_alg) { + Ok(p1363) => p1363, + Err(_) => sig, + } + } else { + sig + }; + + let Some(validator) = async_validator_for_signing_alg(signing_alg) else { + return Err(CertificateTrustError::InvalidCertificate); + }; + + let result = validator + .validate_async(&adjusted_sig, &data, certificate_public_key.raw.as_ref()) + .await; + + match result { + Ok(()) => Ok(true), + Err(RawSignatureValidationError::SignatureMismatch) => Ok(false), + Err(_err) => Err(CertificateTrustError::InvalidCertificate), + } +} + +const RSASSA_PSS_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .10); +const ECDSA_WITH_SHA256_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .2); +const ECDSA_WITH_SHA384_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .3); +const ECDSA_WITH_SHA512_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .4); +const SHA256_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .11); +const SHA384_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .12); +const SHA512_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .13); +const ED25519_OID: Oid<'static> = oid!(1.3.101 .112); +const SHA256_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .1); +const SHA384_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .2); +const SHA512_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .3); diff --git a/internal/crypto/src/webcrypto/mod.rs b/internal/crypto/src/webcrypto/mod.rs index de9ae2b80..4f51d7848 100644 --- a/internal/crypto/src/webcrypto/mod.rs +++ b/internal/crypto/src/webcrypto/mod.rs @@ -25,6 +25,7 @@ pub use async_validators::{ AsyncRawSignatureValidator, }; +pub(crate) mod check_certificate_trust; pub(crate) mod signers; pub mod validators; diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 41bce807f..8a1277057 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -32,7 +32,7 @@ file_io = ["openssl_sign"] serialize_thumbnails = [] no_interleaved_io = ["file_io"] fetch_remote_manifests = [] -openssl = ["dep:openssl", "c2pa-crypto/openssl"] +openssl = ["c2pa-crypto/openssl"] openssl_sign = ["openssl", "c2pa-crypto/openssl"] json_schema = ["dep:schemars", "c2pa-crypto/json_schema"] pdf = ["dep:lopdf"] @@ -135,15 +135,10 @@ image = { version = "0.24.7", default-features = false, features = [ "jpeg", "png", ], optional = true } -instant = "0.1.12" -openssl = { version = "0.10.61", features = ["vendored"], optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] console_log = { version = "1.0.0", features = ["color"] } getrandom = { version = "0.2.7", features = ["js"] } -# We need to use the `inaccurate` flag here to ensure usage of the JavaScript Date API -# to handle certificate timestamp checking correctly. -instant = { version = "0.1.12", features = ["wasm-bindgen", "inaccurate"] } js-sys = "0.3.58" rand_core = "0.9.0-alpha.2" rsa = { version = "0.9.6", features = ["sha2"] } diff --git a/sdk/src/claim.rs b/sdk/src/claim.rs index 8e19ef65a..51b674a8e 100644 --- a/sdk/src/claim.rs +++ b/sdk/src/claim.rs @@ -16,7 +16,7 @@ use std::path::Path; use std::{collections::HashMap, fmt}; use async_generic::async_generic; -use c2pa_crypto::{base64, cose::CertificateAcceptancePolicy, ValidationInfo}; +use c2pa_crypto::{base64, cose::CertificateTrustPolicy, ValidationInfo}; use c2pa_status_tracker::{log_item, OneShotStatusTracker, StatusTracker}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -1040,7 +1040,7 @@ impl Claim { asset_data: &mut ClaimAssetData<'_>, is_provenance: bool, cert_check: bool, - cap: &CertificateAcceptancePolicy, + ctp: &CertificateTrustPolicy, validation_log: &mut impl StatusTracker, ) -> Result<()> { // Parse COSE signed data (signature) and validate it. @@ -1068,10 +1068,10 @@ impl Claim { } // check certificate revocation - check_ocsp_status_async(&sig, &data, cap, validation_log).await?; + check_ocsp_status_async(&sig, &data, ctp, validation_log).await?; let verified = - verify_cose_async(sig, data, additional_bytes, cert_check, cap, validation_log).await; + verify_cose_async(sig, data, additional_bytes, cert_check, ctp, validation_log).await; Claim::verify_internal(claim, asset_data, is_provenance, verified, validation_log) } @@ -1084,7 +1084,7 @@ impl Claim { asset_data: &mut ClaimAssetData<'_>, is_provenance: bool, cert_check: bool, - cap: &CertificateAcceptancePolicy, + ctp: &CertificateTrustPolicy, validation_log: &mut impl StatusTracker, ) -> Result<()> { // Parse COSE signed data (signature) and validate it. @@ -1113,14 +1113,14 @@ impl Claim { }; // check certificate revocation - check_ocsp_status(sig, data, cap, validation_log)?; + check_ocsp_status(sig, data, ctp, validation_log)?; let verified = verify_cose( sig, data, &additional_bytes, cert_check, - cap, + ctp, validation_log, ); diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 4d799ccfa..873e3fa6f 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -16,7 +16,11 @@ #![deny(missing_docs)] use async_generic::async_generic; -use c2pa_crypto::{cose::CertificateAcceptancePolicy, p1363::parse_ec_der_sig, SigningAlg}; +use c2pa_crypto::{ + cose::{check_certificate_profile, CertificateTrustPolicy}, + p1363::parse_ec_der_sig, + SigningAlg, +}; use c2pa_status_tracker::OneShotStatusTracker; use ciborium::value::Value; use coset::{ @@ -27,7 +31,7 @@ use coset::{ use crate::{ claim::Claim, - cose_validator::{check_cert, verify_cose}, + cose_validator::verify_cose, settings::get_settings_value, time_stamp::{ cose_timestamp_countersign, cose_timestamp_countersign_async, make_cose_timestamp, @@ -72,7 +76,7 @@ pub fn sign_claim(claim_bytes: &[u8], signer: &dyn Signer, box_size: usize) -> R Ok(signed_bytes) => { // Sanity check: Ensure that this signature is valid. let mut cose_log = OneShotStatusTracker::default(); - let passthrough_cap = CertificateAcceptancePolicy::default(); + let passthrough_cap = CertificateTrustPolicy::default(); match verify_cose( &signed_bytes, @@ -99,14 +103,19 @@ pub fn sign_claim(claim_bytes: &[u8], signer: &dyn Signer, box_size: usize) -> R fn signing_cert_valid(signing_cert: &[u8]) -> Result<()> { // make sure signer certs are valid let mut cose_log = OneShotStatusTracker::default(); - let mut passthrough_cap = CertificateAcceptancePolicy::default(); + let mut passthrough_cap = CertificateTrustPolicy::default(); // allow user EKUs through this check if configured if let Ok(Some(trust_config)) = get_settings_value::>("trust.trust_config") { passthrough_cap.add_valid_ekus(trust_config.as_bytes()); } - check_cert(signing_cert, &passthrough_cap, &mut cose_log, None) + Ok(check_certificate_profile( + signing_cert, + &passthrough_cap, + &mut cose_log, + None, + )?) } /// Returns signed Cose_Sign1 bytes for `data`. diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index 531bb8742..c7f07cd33 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -13,12 +13,12 @@ use std::io::Cursor; -use asn1_rs::{Any, Class, Header, Tag}; use async_generic::async_generic; use c2pa_crypto::{ asn1::rfc3161::TstInfo, cose::{ - parse_and_validate_sigtst, parse_and_validate_sigtst_async, CertificateAcceptancePolicy, + check_certificate_profile, parse_and_validate_sigtst, parse_and_validate_sigtst_async, + CertificateTrustError, CertificateTrustPolicy, }, ocsp::OcspResponse, p1363::parse_ec_der_sig, @@ -28,46 +28,19 @@ use c2pa_crypto::{ }; use c2pa_status_tracker::{log_item, validation_codes::*, StatusTracker}; use ciborium::value::Value; -use conv::*; use coset::{ iana::{self, EnumI64}, sig_structure_data, Label, TaggedCborSerializable, }; -use x509_parser::{ - der_parser::{ber::parse_ber_sequence, oid}, - num_bigint::BigUint, - oid_registry::Oid, - prelude::*, -}; +use x509_parser::{der_parser::oid, num_bigint::BigUint, oid_registry::Oid, prelude::*}; -#[cfg(feature = "openssl")] -use crate::openssl::verify_trust; -#[cfg(target_arch = "wasm32")] -use crate::wasm::webpki_trust_handler::verify_trust_async; use crate::{ error::{Error, Result}, settings::get_settings_value, }; -pub(crate) const RSA_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .1); -pub(crate) const EC_PUBLICKEY_OID: Oid<'static> = oid!(1.2.840 .10045 .2 .1); -pub(crate) const RSASSA_PSS_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .10); - -pub(crate) const ECDSA_WITH_SHA256_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .2); -pub(crate) const ECDSA_WITH_SHA384_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .3); -pub(crate) const ECDSA_WITH_SHA512_OID: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .4); -pub(crate) const SHA256_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .11); -pub(crate) const SHA384_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .12); -pub(crate) const SHA512_WITH_RSAENCRYPTION_OID: Oid<'static> = oid!(1.2.840 .113549 .1 .1 .13); -pub(crate) const ED25519_OID: Oid<'static> = oid!(1.3.101 .112); #[allow(dead_code)] // used only in WASM build pub(crate) const SHA1_OID: Oid<'static> = oid!(1.3.14 .3 .2 .26); -pub(crate) const SHA256_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .1); -pub(crate) const SHA384_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .2); -pub(crate) const SHA512_OID: Oid<'static> = oid!(2.16.840 .1 .101 .3 .4 .2 .3); -pub(crate) const SECP521R1_OID: Oid<'static> = oid!(1.3.132 .0 .35); -pub(crate) const SECP384R1_OID: Oid<'static> = oid!(1.3.132 .0 .34); -pub(crate) const PRIME256V1_OID: Oid<'static> = oid!(1.2.840 .10045 .3 .1 .7); /********************** Supported Validators *************************************** RS256 RSASSA-PKCS1-v1_5 using SHA-256 - not recommended @@ -106,403 +79,6 @@ fn get_cose_sign1( } } -pub(crate) fn check_cert( - ca_der_bytes: &[u8], - cap: &CertificateAcceptancePolicy, - validation_log: &mut impl StatusTracker, - _tst_info_opt: Option<&TstInfo>, -) -> Result<()> { - // get the cert in der format - let (_rem, signcert) = X509Certificate::from_der(ca_der_bytes).map_err(|_err| { - log_item!( - "Cose_Sign1", - "certificate could not be parsed", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - Error::CoseInvalidCert - })?; - - // cert version must be 3 - if signcert.version() != X509Version::V3 { - log_item!( - "Cose_Sign1", - "certificate version incorrect", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - - // check for cert expiration - if let Some(tst_info) = _tst_info_opt { - // was there a time stamp association with this signature, is verify against that time - let signing_time = gt_to_datetime(tst_info.gen_time.clone()); - if !signcert.validity().is_valid_at( - x509_parser::time::ASN1Time::from_timestamp(signing_time.timestamp()) - .map_err(|_| Error::CoseInvalidCert)?, - ) { - log_item!("Cose_Sign1", "certificate expired", "check_cert_alg") - .validation_status(SIGNING_CREDENTIAL_EXPIRED) - .failure_no_throw(validation_log, Error::CoseCertExpiration); - - return Err(Error::CoseCertExpiration); - } - } else { - // no timestamp so check against current time - // use instant to avoid wasm issues - let now_f64 = instant::now() / 1000.0; - let now: i64 = now_f64 - .approx_as::() - .map_err(|_e| Error::BadParam("system time invalid".to_string()))?; - - if !signcert.validity().is_valid_at( - x509_parser::time::ASN1Time::from_timestamp(now).map_err(|_| Error::CoseInvalidCert)?, - ) { - log_item!("Cose_Sign1", "certificate expired", "check_cert_alg") - .validation_status(SIGNING_CREDENTIAL_EXPIRED) - .failure_no_throw(validation_log, Error::CoseCertExpiration); - - return Err(Error::CoseCertExpiration); - } - } - - let cert_alg = signcert.signature_algorithm.algorithm.clone(); - - // check algorithm needed from cert - - // cert must be signed with one the following algorithm - if !(cert_alg == SHA256_WITH_RSAENCRYPTION_OID - || cert_alg == SHA384_WITH_RSAENCRYPTION_OID - || cert_alg == SHA512_WITH_RSAENCRYPTION_OID - || cert_alg == ECDSA_WITH_SHA256_OID - || cert_alg == ECDSA_WITH_SHA384_OID - || cert_alg == ECDSA_WITH_SHA512_OID - || cert_alg == RSASSA_PSS_OID - || cert_alg == ED25519_OID) - { - log_item!( - "Cose_Sign1", - "certificate algorithm not supported", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - - // verify rsassa_pss parameters - if cert_alg == RSASSA_PSS_OID { - if let Some(parameters) = &signcert.signature_algorithm.parameters { - let seq = parameters - .as_sequence() - .map_err(|_err| Error::CoseInvalidCert)?; - - let (_i, (ha_alg, mgf_ai)) = seq - .parse(|i| { - let (i, h) =
::from_der(i)?; - if h.class() != Class::ContextSpecific || h.tag() != Tag(0) { - return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); - } - - let (i, ha_alg) = AlgorithmIdentifier::from_der(i) - .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; - - let (i, h) =
::from_der(i)?; - if h.class() != Class::ContextSpecific || h.tag() != Tag(1) { - return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); - } - - let (i, mgf_ai) = AlgorithmIdentifier::from_der(i) - .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; - - // Ignore anything that follows these two parameters. - - Ok((i, (ha_alg, mgf_ai))) - }) - .map_err(|_| Error::CoseInvalidCert)?; - - let mgf_ai_parameters = mgf_ai.parameters.ok_or(Error::CoseInvalidCert)?; - let mgf_ai_parameters = mgf_ai_parameters - .as_sequence() - .map_err(|_| Error::CoseInvalidCert)?; - - let (_i, mgf_ai_params_algorithm) = - ::from_der(&mgf_ai_parameters.content) - .map_err(|_| Error::CoseInvalidCert)?; - - let mgf_ai_params_algorithm = mgf_ai_params_algorithm - .as_oid() - .map_err(|_| Error::CoseInvalidCert)?; - - // must be the same - if ha_alg.algorithm.to_id_string() != mgf_ai_params_algorithm.to_id_string() { - log_item!( - "Cose_Sign1", - "certificate algorithm error", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - - // check for one of the mandatory types - if !(ha_alg.algorithm == SHA256_OID - || ha_alg.algorithm == SHA384_OID - || ha_alg.algorithm == SHA512_OID) - { - log_item!( - "Cose_Sign1", - "certificate hash algorithm not supported", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - } else { - log_item!( - "Cose_Sign1", - "certificate missing algorithm parameters", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - } - - // check curves for SPKI EC algorithms - let pk = signcert.public_key(); - let skpi_alg = &pk.algorithm; - - if skpi_alg.algorithm == EC_PUBLICKEY_OID { - if let Some(parameters) = &skpi_alg.parameters { - let named_curve_oid = parameters.as_oid().map_err(|_err| Error::CoseInvalidCert)?; - - // must be one of these named curves - if !(named_curve_oid == PRIME256V1_OID - || named_curve_oid == SECP384R1_OID - || named_curve_oid == SECP521R1_OID) - { - log_item!( - "Cose_Sign1", - "certificate unsupported EC curve", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - } else { - return Err(Error::CoseInvalidCert); - } - } - - // check modulus minimum length (for RSA & PSS algorithms) - if skpi_alg.algorithm == RSA_OID || skpi_alg.algorithm == RSASSA_PSS_OID { - let (_, skpi_ber) = parse_ber_sequence(&pk.subject_public_key.data) - .map_err(|_err| Error::CoseInvalidCert)?; - - let seq = skpi_ber - .as_sequence() - .map_err(|_err| Error::CoseInvalidCert)?; - if seq.len() < 2 { - return Err(Error::CoseInvalidCert); - } - - let modulus = seq[0].as_bigint().map_err(|_| Error::CoseInvalidCert)?; - - if modulus.bits() < 2048 { - log_item!( - "Cose_Sign1", - "certificate key length too short", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - } - - // check cert values - let tbscert = &signcert.tbs_certificate; - - let is_self_signed = tbscert.is_ca() && tbscert.issuer() == tbscert.subject(); - - // self signed certs are disallowed - if is_self_signed { - log_item!( - "Cose_Sign1", - "certificate issuer and subject cannot be the same {self-signed disallowed}", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - - // unique ids are not allowed - if signcert.issuer_uid.is_some() || signcert.subject_uid.is_some() { - log_item!( - "Cose_Sign1", - "certificate issuer/subject unique ids are not allowed", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - - let mut aki_good = false; - let mut ski_good = false; - let mut key_usage_good = false; - let mut handled_all_critical = true; - let extended_key_usage_good = match tbscert - .extended_key_usage() - .map_err(|_| Error::CoseInvalidCert)? - { - Some(BasicExtension { value: eku, .. }) => { - if eku.any { - log_item!( - "Cose_Sign1", - "certificate 'any' EKU not allowed", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - - if cap.has_allowed_eku(eku).is_none() { - log_item!( - "Cose_Sign1", - "certificate missing required EKU", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - - // one or the other || either of these two, and no others field - if (eku.ocsp_signing && eku.time_stamping) - || ((eku.ocsp_signing ^ eku.time_stamping) - && (eku.client_auth - | eku.code_signing - | eku.email_protection - | eku.server_auth - | !eku.other.is_empty())) - { - log_item!( - "Cose_Sign1", - "certificate invalid set of EKUs", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - - true - } - None => tbscert.is_ca(), // if is not ca it must be present - }; - - // populate needed extension info - for e in signcert.extensions() { - match e.parsed_extension() { - ParsedExtension::AuthorityKeyIdentifier(_aki) => { - aki_good = true; - } - ParsedExtension::SubjectKeyIdentifier(_spki) => { - ski_good = true; - } - ParsedExtension::KeyUsage(ku) => { - if ku.digital_signature() { - if ku.key_cert_sign() && !tbscert.is_ca() { - log_item!( - "Cose_Sign1", - "certificate missing digitalSignature EKU", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - return Err(Error::CoseInvalidCert); - } - key_usage_good = true; - } - if ku.key_cert_sign() || ku.non_repudiation() { - key_usage_good = true; - } - // todo: warn if not marked critical - // if !e.critical { // warn here somehow} - } - ParsedExtension::CertificatePolicies(_) => (), - ParsedExtension::PolicyMappings(_) => (), - ParsedExtension::SubjectAlternativeName(_) => (), - ParsedExtension::BasicConstraints(_) => (), - ParsedExtension::NameConstraints(_) => (), - ParsedExtension::PolicyConstraints(_) => (), - ParsedExtension::ExtendedKeyUsage(_) => (), - ParsedExtension::CRLDistributionPoints(_) => (), - ParsedExtension::InhibitAnyPolicy(_) => (), - ParsedExtension::AuthorityInfoAccess(_) => (), - ParsedExtension::NSCertType(_) => (), - ParsedExtension::CRLNumber(_) => (), - ParsedExtension::ReasonCode(_) => (), - ParsedExtension::InvalidityDate(_) => (), - ParsedExtension::Unparsed => { - if e.critical { - // unhandled critical extension - handled_all_critical = false; - } - } - _ => { - if e.critical { - // unhandled critical extension - handled_all_critical = false; - } - } - } - } - - // if cert is a CA must have valid SubjectKeyIdentifier - ski_good = if tbscert.is_ca() { ski_good } else { true }; - - // check all flags - if aki_good && ski_good && key_usage_good && extended_key_usage_good && handled_all_critical { - Ok(()) - } else { - log_item!( - "Cose_Sign1", - "certificate params incorrect", - "check_cert_alg" - ) - .validation_status(SIGNING_CREDENTIAL_INVALID) - .failure_no_throw(validation_log, Error::CoseInvalidCert); - - Err(Error::CoseInvalidCert) - } -} - pub(crate) fn get_signing_alg(cs1: &coset::CoseSign1) -> Result { // find the supported handler for the algorithm match cs1.protected.header.alg { @@ -679,7 +255,7 @@ fn get_ocsp_der(sign1: &coset::CoseSign1) -> Option> { pub(crate) fn check_ocsp_status( cose_bytes: &[u8], data: &[u8], - cap: &CertificateAcceptancePolicy, + ctp: &CertificateTrustPolicy, validation_log: &mut impl StatusTracker, ) -> Result { let sign1 = get_cose_sign1(cose_bytes, data, validation_log)?; @@ -706,7 +282,12 @@ pub(crate) fn check_ocsp_status( // if we get a valid response validate the certs if ocsp_data.revoked_at.is_none() { if let Some(ocsp_certs) = &ocsp_data.ocsp_certs { - check_cert(&ocsp_certs[0], cap, validation_log, Some(tst_info))?; + check_certificate_profile( + &ocsp_certs[0], + ctp, + validation_log, + Some(tst_info), + )?; } } result = Ok(ocsp_data); @@ -744,7 +325,12 @@ pub(crate) fn check_ocsp_status( // if we get a valid response validate the certs if ocsp_data.revoked_at.is_none() { if let Some(ocsp_certs) = &ocsp_data.ocsp_certs { - check_cert(&ocsp_certs[0], cap, validation_log, None)?; + check_certificate_profile( + &ocsp_certs[0], + ctp, + validation_log, + None, + )?; } } result = Ok(ocsp_data); @@ -825,18 +411,18 @@ fn get_timestamp_info(sign1: &coset::CoseSign1, data: &[u8]) -> Result } #[async_generic(async_signature( - cap: &CertificateAcceptancePolicy, + ctp: &CertificateTrustPolicy, chain_der: &[Vec], cert_der: &[u8], - signing_time_epoc: Option, + signing_time_epoch: Option, validation_log: &mut impl StatusTracker ))] #[allow(unused)] fn check_trust( - cap: &CertificateAcceptancePolicy, + ctp: &CertificateTrustPolicy, chain_der: &[Vec], cert_der: &[u8], - signing_time_epoc: Option, + signing_time_epoch: Option, validation_log: &mut impl StatusTracker, ) -> Result<()> { // just return is trust checks are disabled or misconfigured @@ -849,54 +435,27 @@ fn check_trust( Err(e) => return Err(e), } - // is the certificate trusted - - let verify_result: Result = if _sync { - #[cfg(not(feature = "openssl"))] - { - Err(Error::NotImplemented( - "no trust handler for this feature".to_string(), - )) - } - - #[cfg(feature = "openssl")] - { - verify_trust(cap, chain_der, cert_der, signing_time_epoc) - } + let verify_result = if _sync { + ctp.check_certificate_trust(chain_der, cert_der, signing_time_epoch) } else { - #[cfg(target_arch = "wasm32")] - { - verify_trust_async(cap, chain_der, cert_der, signing_time_epoc).await - } - - #[cfg(feature = "openssl")] - { - verify_trust(cap, chain_der, cert_der, signing_time_epoc) - } - - #[cfg(not(any(feature = "openssl", target_arch = "wasm32")))] - { - Err(Error::NotImplemented( - "no trust handler for this feature".to_string(), - )) - } + ctp.check_certificate_trust_async(chain_der, cert_der, signing_time_epoch) + .await }; match verify_result { - Ok(trusted) => { - if trusted { - log_item!("Cose_Sign1", "signing certificate trusted", "verify_cose") - .validation_status(SIGNING_CREDENTIAL_TRUSTED) - .success(validation_log); + Ok(()) => { + log_item!("Cose_Sign1", "signing certificate trusted", "verify_cose") + .validation_status(SIGNING_CREDENTIAL_TRUSTED) + .success(validation_log); - Ok(()) - } else { - log_item!("Cose_Sign1", "signing certificate untrusted", "verify_cose") - .validation_status(SIGNING_CREDENTIAL_UNTRUSTED) - .failure_no_throw(validation_log, Error::CoseCertUntrusted); + Ok(()) + } + Err(CertificateTrustError::CertificateNotTrusted) => { + log_item!("Cose_Sign1", "signing certificate untrusted", "verify_cose") + .validation_status(SIGNING_CREDENTIAL_UNTRUSTED) + .failure_no_throw(validation_log, Error::CoseCertUntrusted); - Err(Error::CoseCertUntrusted) - } + Err(Error::CoseCertUntrusted) } Err(e) => { log_item!("Cose_Sign1", "signing certificate untrusted", "verify_cose") @@ -905,7 +464,7 @@ fn check_trust( // TO REVIEW: Mixed message: Are we using CoseCertUntrusted in log or &e from above? // validation_log.log(log_item, Error::CoseCertUntrusted)?; - Err(e) + Err(e.into()) } } } @@ -967,7 +526,7 @@ pub(crate) async fn verify_cose_async( data: Vec, additional_data: Vec, cert_check: bool, - cap: &CertificateAcceptancePolicy, + ctp: &CertificateTrustPolicy, validation_log: &mut impl StatusTracker, ) -> Result { let mut sign1 = get_cose_sign1(&cose_bytes, &data, validation_log)?; @@ -1003,11 +562,15 @@ pub(crate) async fn verify_cose_async( if cert_check { // verify certs match &tst_info_res { - Ok(tst_info) => check_cert(der_bytes, cap, validation_log, Some(tst_info))?, + Ok(tst_info) => { + check_certificate_profile(der_bytes, ctp, validation_log, Some(tst_info))? + } Err(e) => { // log timestamp errors match e { - Error::NotFound => check_cert(der_bytes, cap, validation_log, None)?, + Error::NotFound => { + check_certificate_profile(der_bytes, ctp, validation_log, None)? + } Error::TimeStampError(TimeStampError::InvalidData) => { log_item!( @@ -1038,7 +601,7 @@ pub(crate) async fn verify_cose_async( // is the certificate trusted #[cfg(target_arch = "wasm32")] check_trust_async( - cap, + ctp, &certs[1..], der_bytes, tst_info_result_to_timestamp(&tst_info_res), @@ -1048,7 +611,7 @@ pub(crate) async fn verify_cose_async( #[cfg(not(target_arch = "wasm32"))] check_trust( - cap, + ctp, &certs[1..], der_bytes, tst_info_result_to_timestamp(&tst_info_res), @@ -1170,7 +733,7 @@ pub(crate) fn verify_cose( data: &[u8], additional_data: &[u8], cert_check: bool, - cap: &CertificateAcceptancePolicy, + ctp: &CertificateTrustPolicy, validation_log: &mut impl StatusTracker, ) -> Result { let sign1 = get_cose_sign1(cose_bytes, data, validation_log)?; @@ -1208,11 +771,15 @@ pub(crate) fn verify_cose( if cert_check { // verify certs match &tst_info_res { - Ok(tst_info) => check_cert(der_bytes, cap, validation_log, Some(tst_info))?, + Ok(tst_info) => { + check_certificate_profile(der_bytes, ctp, validation_log, Some(tst_info))? + } Err(e) => { // log timestamp errors match e { - Error::NotFound => check_cert(der_bytes, cap, validation_log, None)?, + Error::NotFound => { + check_certificate_profile(der_bytes, ctp, validation_log, None)? + } Error::TimeStampError(TimeStampError::InvalidData) => { log_item!( @@ -1250,7 +817,7 @@ pub(crate) fn verify_cose( // is the certificate trusted check_trust( - cap, + ctp, &certs[1..], der_bytes, tst_info_result_to_timestamp(&tst_info_res), @@ -1369,67 +936,11 @@ pub mod tests { use c2pa_crypto::SigningAlg; use c2pa_status_tracker::DetailedStatusTracker; use sha2::digest::generic_array::sequence::Shorten; + use x509_parser::{certificate::X509Certificate, pem::Pem}; use super::*; use crate::{utils::test_signer::test_signer, Signer}; - #[test] - #[cfg(feature = "file_io")] - fn test_expired_cert() { - let mut validation_log = DetailedStatusTracker::default(); - let cap = CertificateAcceptancePolicy::default(); - - let mut cert_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); - cert_path.push("tests/fixtures/rsa-pss256_key-expired.pub"); - - let expired_cert = std::fs::read(&cert_path).unwrap(); - - if let Ok(signcert) = openssl::x509::X509::from_pem(&expired_cert) { - let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert(&der_bytes, &cap, &mut validation_log, None).is_err()); - - assert!(!validation_log.logged_items().is_empty()); - - assert_eq!( - validation_log.logged_items()[0].validation_status, - Some(SIGNING_CREDENTIAL_EXPIRED.into()) - ); - } - } - - #[test] - #[cfg(all(feature = "openssl_sign", feature = "file_io"))] - fn test_cert_algorithms() { - let cap = CertificateAcceptancePolicy::default(); - - let mut validation_log = DetailedStatusTracker::default(); - - let es256_cert = include_bytes!("../tests/fixtures/certs/es256.pub"); - let es384_cert = include_bytes!("../tests/fixtures/certs/es384.pub"); - let es512_cert = include_bytes!("../tests/fixtures/certs/es512.pub"); - let rsa_pss256_cert = include_bytes!("../tests/fixtures/certs/ps256.pub"); - - if let Ok(signcert) = openssl::x509::X509::from_pem(es256_cert) { - let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert(&der_bytes, &cap, &mut validation_log, None).is_ok()); - } - - if let Ok(signcert) = openssl::x509::X509::from_pem(es384_cert) { - let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert(&der_bytes, &cap, &mut validation_log, None).is_ok()); - } - - if let Ok(signcert) = openssl::x509::X509::from_pem(es512_cert) { - let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert(&der_bytes, &cap, &mut validation_log, None).is_ok()); - } - - if let Ok(signcert) = openssl::x509::X509::from_pem(rsa_pss256_cert) { - let der_bytes = signcert.to_der().unwrap(); - assert!(check_cert(&der_bytes, &cap, &mut validation_log, None).is_ok()); - } - } - #[test] fn test_no_timestamp() { let mut validation_log = DetailedStatusTracker::default(); diff --git a/sdk/src/error.rs b/sdk/src/error.rs index e9cef3bd4..ed3db8ea0 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -288,10 +288,6 @@ pub enum Error { #[error("could not acquire OpenSSL FFI mutex")] OpenSslMutexError, - #[cfg(feature = "openssl")] - #[error(transparent)] - OpenSslError(#[from] openssl::error::ErrorStack), - #[error(transparent)] OtherError(#[from] Box), @@ -313,6 +309,12 @@ pub enum Error { #[error(transparent)] RawSignerError(#[from] c2pa_crypto::raw_signature::RawSignerError), + #[error(transparent)] + CertificateProfileError(#[from] c2pa_crypto::cose::CertificateProfileError), + + #[error(transparent)] + CertificateTrustError(#[from] c2pa_crypto::cose::CertificateTrustError), + #[error(transparent)] InvalidCertificateError(#[from] c2pa_crypto::cose::InvalidCertificateError), } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 2cc22b9e7..c291f2f2d 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -163,8 +163,6 @@ pub(crate) mod manifest; pub(crate) mod manifest_assertion; pub(crate) mod manifest_store; pub(crate) mod manifest_store_report; -#[cfg(feature = "openssl")] -pub(crate) mod openssl; #[allow(dead_code)] // TODO: Remove this when the feature is released (used in tests only for some builds now) pub(crate) mod reader; diff --git a/sdk/src/openssl/mod.rs b/sdk/src/openssl/mod.rs deleted file mode 100644 index 12b03d0d1..000000000 --- a/sdk/src/openssl/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2022 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -#[cfg(feature = "openssl")] -mod openssl_trust_handler; - -#[cfg(feature = "openssl")] -pub(crate) use openssl_trust_handler::verify_trust; diff --git a/sdk/src/openssl/openssl_trust_handler.rs b/sdk/src/openssl/openssl_trust_handler.rs deleted file mode 100644 index f8e6b1602..000000000 --- a/sdk/src/openssl/openssl_trust_handler.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2022 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -use c2pa_crypto::{cose::CertificateAcceptancePolicy, openssl::OpenSslMutex}; -use openssl::x509::verify::X509VerifyFlags; - -use crate::Error; - -fn certs_der_to_x509(ders: &[Vec]) -> crate::Result> { - // IMPORTANT: ffi_mutex::acquire() should have been called by calling fn. Please - // don't make this pub or pub(crate) without finding a way to ensure that - // precondition. - - let mut certs: Vec = Vec::new(); - - for d in ders { - let cert = openssl::x509::X509::from_der(d)?; - certs.push(cert); - } - - Ok(certs) -} - -// verify certificate and trust chain -pub(crate) fn verify_trust( - cap: &CertificateAcceptancePolicy, - chain_der: &[Vec], - cert_der: &[u8], - signing_time_epoc: Option, -) -> crate::Result { - // check the cert against the allowed list first - // TO DO: optimize by hashing the cert? - if cap.end_entity_cert_ders().any(|der| der == cert_der) { - return Ok(true); - } - - let _openssl = OpenSslMutex::acquire()?; - - let mut cert_chain = openssl::stack::Stack::new().map_err(Error::OpenSslError)?; - let mut store_ctx = openssl::x509::X509StoreContext::new().map_err(Error::OpenSslError)?; - - let chain = certs_der_to_x509(chain_der)?; - for c in chain { - cert_chain.push(c).map_err(Error::OpenSslError)?; - } - let cert = openssl::x509::X509::from_der(cert_der).map_err(Error::OpenSslError)?; - - let mut builder = openssl::x509::store::X509StoreBuilder::new().map_err(Error::OpenSslError)?; - - let mut verify_param = - openssl::x509::verify::X509VerifyParam::new().map_err(Error::OpenSslError)?; - if let Some(st) = signing_time_epoc { - verify_param.set_time(st); - } else { - verify_param - .set_flags(X509VerifyFlags::NO_CHECK_TIME) - .map_err(Error::OpenSslError)?; - } - builder - .set_param(&verify_param) - .map_err(Error::OpenSslError)?; - - // add trust anchors - let mut has_anchors = false; - for der in cap.trust_anchor_ders() { - let c = openssl::x509::X509::from_der(der).map_err(Error::OpenSslError)?; - builder.add_cert(c)?; - has_anchors = true - } - - // finalize store - let store = builder.build(); - - if !has_anchors { - return Ok(false); - } - - match store_ctx.init(&store, cert.as_ref(), &cert_chain, |f| f.verify_cert()) { - Ok(trust) => Ok(trust), - Err(_) => Ok(false), - } -} - -#[cfg(test)] -#[cfg(feature = "file_io")] -pub mod tests { - #![allow(clippy::expect_used)] - #![allow(clippy::panic)] - #![allow(clippy::unwrap_used)] - - use c2pa_crypto::SigningAlg; - - use super::*; - use crate::{utils::test_signer::test_signer, Signer}; - - #[test] - fn test_trust_store() { - let cap = crate::utils::test::test_certificate_acceptance_policy(); - - // test all the certs - let ps256 = test_signer(SigningAlg::Ps256); - let ps384 = test_signer(SigningAlg::Ps384); - let ps512 = test_signer(SigningAlg::Ps512); - let es256 = test_signer(SigningAlg::Es256); - let es384 = test_signer(SigningAlg::Es384); - let es512 = test_signer(SigningAlg::Es512); - let ed25519 = test_signer(SigningAlg::Ed25519); - - let ps256_certs = ps256.certs().unwrap(); - let ps384_certs = ps384.certs().unwrap(); - let ps512_certs = ps512.certs().unwrap(); - let es256_certs = es256.certs().unwrap(); - let es384_certs = es384.certs().unwrap(); - let es512_certs = es512.certs().unwrap(); - let ed25519_certs = ed25519.certs().unwrap(); - - assert!(verify_trust(&cap, &ps256_certs[1..], &ps256_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &ps384_certs[1..], &ps384_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &ps512_certs[1..], &ps512_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &es256_certs[1..], &es256_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &es384_certs[1..], &es384_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &es512_certs[1..], &es512_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &ed25519_certs[1..], &ed25519_certs[0], None).unwrap()); - } - - #[test] - fn test_broken_trust_chain() { - let cap = CertificateAcceptancePolicy::default(); - - // test all the certs - let ps256 = test_signer(SigningAlg::Ps256); - let ps384 = test_signer(SigningAlg::Ps384); - let ps512 = test_signer(SigningAlg::Ps512); - let es256 = test_signer(SigningAlg::Es256); - let es384 = test_signer(SigningAlg::Es384); - let es512 = test_signer(SigningAlg::Es512); - let ed25519 = test_signer(SigningAlg::Ed25519); - - let ps256_certs = ps256.certs().unwrap(); - let ps384_certs = ps384.certs().unwrap(); - let ps512_certs = ps512.certs().unwrap(); - let es256_certs = es256.certs().unwrap(); - let es384_certs = es384.certs().unwrap(); - let es512_certs = es512.certs().unwrap(); - let ed25519_certs = ed25519.certs().unwrap(); - - assert!(!verify_trust(&cap, &ps256_certs[2..], &ps256_certs[0], None).unwrap()); - assert!(!verify_trust(&cap, &ps384_certs[2..], &ps384_certs[0], None).unwrap()); - assert!(!verify_trust(&cap, &ps384_certs[2..], &ps384_certs[0], None).unwrap()); - assert!(!verify_trust(&cap, &ps512_certs[2..], &ps512_certs[0], None).unwrap()); - assert!(!verify_trust(&cap, &es256_certs[2..], &es256_certs[0], None).unwrap()); - assert!(!verify_trust(&cap, &es384_certs[2..], &es384_certs[0], None).unwrap()); - assert!(!verify_trust(&cap, &es512_certs[2..], &es512_certs[0], None).unwrap()); - assert!(!verify_trust(&cap, &ed25519_certs[2..], &ed25519_certs[0], None).unwrap()); - } - - #[test] - fn test_allowed_list() { - let cap = crate::utils::test::test_certificate_acceptance_policy(); - - // test all the certs - let ps256 = test_signer(SigningAlg::Ps256); - let ps384 = test_signer(SigningAlg::Ps384); - let ps512 = test_signer(SigningAlg::Ps512); - let es256 = test_signer(SigningAlg::Es256); - let es384 = test_signer(SigningAlg::Es384); - let es512 = test_signer(SigningAlg::Es512); - let ed25519 = test_signer(SigningAlg::Ed25519); - - let ps256_certs = ps256.certs().unwrap(); - let ps384_certs = ps384.certs().unwrap(); - let ps512_certs = ps512.certs().unwrap(); - let es256_certs = es256.certs().unwrap(); - let es384_certs = es384.certs().unwrap(); - let es512_certs = es512.certs().unwrap(); - let ed25519_certs = ed25519.certs().unwrap(); - - assert!(verify_trust(&cap, &ps256_certs[1..], &ps256_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &ps384_certs[1..], &ps384_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &ps512_certs[1..], &ps512_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &es256_certs[1..], &es256_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &es384_certs[1..], &es384_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &es512_certs[1..], &es512_certs[0], None).unwrap()); - assert!(verify_trust(&cap, &ed25519_certs[1..], &ed25519_certs[0], None).unwrap()); - } - - // TO REVIEW: Do we need this? Considering removing support for hashed certs. - // #[test] - // fn test_allowed_list_hashes() { - // let mut cap = CertificateAcceptancePolicy::default(); - - // cap.add_end_entity_credentials(include_bytes!( - // "../../tests/fixtures/certs/trust/allowed_list.hash" - // )) - // .unwrap(); - - // // test all the certs - // let ps256 = test_signer(SigningAlg::Ps256); - // let ps384 = test_signer(SigningAlg::Ps384); - // let ps512 = test_signer(SigningAlg::Ps512); - // let es256 = test_signer(SigningAlg::Es256); - // let es384 = test_signer(SigningAlg::Es384); - // let es512 = test_signer(SigningAlg::Es512); - // let ed25519 = test_signer(SigningAlg::Ed25519); - - // let ps256_certs = ps256.certs().unwrap(); - // let ps384_certs = ps384.certs().unwrap(); - // let ps512_certs = ps512.certs().unwrap(); - // let es256_certs = es256.certs().unwrap(); - // let es384_certs = es384.certs().unwrap(); - // let es512_certs = es512.certs().unwrap(); - // let ed25519_certs = ed25519.certs().unwrap(); - - // assert!(verify_trust(&cap, &ps256_certs[1..], &ps256_certs[0], None).unwrap()); - // assert!(verify_trust(&cap, &ps384_certs[1..], &ps384_certs[0], None).unwrap()); - // assert!(verify_trust(&cap, &ps512_certs[1..], &ps512_certs[0], None).unwrap()); - // assert!(verify_trust(&cap, &es256_certs[1..], &es256_certs[0], None).unwrap()); - // assert!(verify_trust(&cap, &es384_certs[1..], &es384_certs[0], None).unwrap()); - // assert!(verify_trust(&cap, &es512_certs[1..], &es512_certs[0], None).unwrap()); - // assert!(verify_trust(&cap, &ed25519_certs[1..], &ed25519_certs[0], None).unwrap()); - // } -} diff --git a/sdk/src/openssl/test_cert_root_bundle.pem b/sdk/src/openssl/test_cert_root_bundle.pem deleted file mode 100644 index f6f257d8d..000000000 --- a/sdk/src/openssl/test_cert_root_bundle.pem +++ /dev/null @@ -1,174 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICEzCCAcWgAwIBAgIUW4fUnS38162x10PCnB8qFsrQuZgwBQYDK2VwMHcxCzAJ -BgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJlMRowGAYD -VQQKDBFDMlBBIFRlc3QgUm9vdCBDQTEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05M -WTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yMjA2MTAxODQ2NDFaFw0zMjA2MDcxODQ2 -NDFaMHcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdo -ZXJlMRowGAYDVQQKDBFDMlBBIFRlc3QgUm9vdCBDQTEZMBcGA1UECwwQRk9SIFRF -U1RJTkdfT05MWTEQMA4GA1UEAwwHUm9vdCBDQTAqMAUGAytlcAMhAGPUgK9q1H3D -eKMGqLGjTXJSpsrLpe0kpxkaFMe7KUAuo2MwYTAdBgNVHQ4EFgQUXuZWArP1jiRM -fgye6ZqRyGupTowwHwYDVR0jBBgwFoAUXuZWArP1jiRMfgye6ZqRyGupTowwDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwBQYDK2VwA0EA8E79g54u2fUy -dfVLPyqKmtjenOUMvVQD7waNbetLY7kvUJZCd5eaDghk30/Q1RaNjiP/2RfA/it8 -zGxQnM2hCA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIC2jCCAjygAwIBAgIUYm+LFaltpWbS9kED6RRAamOdUHowCgYIKoZIzj0EAwQw -dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx -GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO -R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTIyMDYxMDE4NDY0MFoXDTMyMDYw -NzE4NDY0MFowdzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlT -b21ld2hlcmUxGjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBG -T1IgVEVTVElOR19PTkxZMRAwDgYDVQQDDAdSb290IENBMIGbMBAGByqGSM49AgEG -BSuBBAAjA4GGAAQBaifSYJBkf5fgH3FWPxRdV84qwIsLd7RcIDcRJrRkan0xUYP5 -zco7R4fFGaQ9YJB8dauyqiNg00LVuPajvKmhgEMAT4eSfEhYC25F2ggXQlBIK3Q7 -mkXwJTIJSObnbw4S9Jy3W6OVKq351VpgWUcmhvGRRejW7S/D8L2tzqRW7JPI2uSj -YzBhMB0GA1UdDgQWBBS6OykommTmfYoLJuPN4OU83wjPqjAfBgNVHSMEGDAWgBS6 -OykommTmfYoLJuPN4OU83wjPqjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBhjAKBggqhkjOPQQDBAOBiwAwgYcCQV4B6uKKoCWecEDlzj2xQLFPmnBQIOzD -nyiSEcYyrCKwMV+HYS39oM+T53NvukLKUTznHwdWc9++HNaqc+IjsDl6AkIB2lXd -5+s3xf0ioU91GJ4E13o5rpAULDxVSrN34A7BlsaXYQLnSkLMqva6E7nq2JBYjkqf -iwNQm1DDcQPtPTnddOs= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICkTCCAhagAwIBAgIUIngKvNC/BMF3TRIafgweprIbGgAwCgYIKoZIzj0EAwMw -dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx -GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO -R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTIyMDYxMDE4NDY0MFoXDTMyMDYw -NzE4NDY0MFowdzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlT -b21ld2hlcmUxGjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBG -T1IgVEVTVElOR19PTkxZMRAwDgYDVQQDDAdSb290IENBMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAEX3FzSTnCcEAP3wteNaiy4GZzZ+ABd2Y7gJpfyZf3kkCuX/I3psFq -QBRvb3/FEBaDT4VbDNlZ0WLwtw5d3PI42Zufgpxemgfjf31d8H51eU3/IfAz5AFX -y/OarhObHgVvo2MwYTAdBgNVHQ4EFgQUe+FK5t6/bQGIcGY6kkeIKTX/bJ0wHwYD -VR0jBBgwFoAUe+FK5t6/bQGIcGY6kkeIKTX/bJ0wDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAPOgmJbVdhDh9KlgQXqE -FzHiCt347JG4strk22MXzOgxQ0LnXStIh+viC3S1INzuBgIxAI1jiUBX/V7Gg0y6 -Y/p6a63Xp2w+ia7vlUaUBWsR3ex9NNSTPLNoDkoTCSDOE2O20w== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICUzCCAfmgAwIBAgIUdmkq4byvgk2FSnddHqB2yjoD68gwCgYIKoZIzj0EAwIw -dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx -GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO -R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTIyMDYxMDE4NDY0MFoXDTMyMDYw -NzE4NDY0MFowdzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlT -b21ld2hlcmUxGjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBG -T1IgVEVTVElOR19PTkxZMRAwDgYDVQQDDAdSb290IENBMFkwEwYHKoZIzj0CAQYI -KoZIzj0DAQcDQgAEre/KpcWwGEHt+mD4xso3xotRnRx2IEsMoYwVIKI7iEJrDEye -PcvJuBywA0qiMw2yvAvGOzW/fqUTu1jABrFIk6NjMGEwHQYDVR0OBBYEFF6ZuIbh -eBvZVxVadQBStikOy6iMMB8GA1UdIwQYMBaAFF6ZuIbheBvZVxVadQBStikOy6iM -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0gA -MEUCIHBC1xLwkCWSGhVXFlSnQBx9cGZivXzCbt8BuwRqPSUoAiEAteZQDk685yh9 -jgOTkp4H8oAmM1As+qlkRK2b+CHAQ3k= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- diff --git a/sdk/src/salt.rs b/sdk/src/salt.rs index c84508d5f..09aa1e3c8 100644 --- a/sdk/src/salt.rs +++ b/sdk/src/salt.rs @@ -55,20 +55,11 @@ impl Default for DefaultSalt { impl SaltGenerator for DefaultSalt { fn generate_salt(&self) -> Option> { - #[cfg(feature = "openssl_sign")] + #[cfg(target_arch = "wasm32")] { - let mut salt = vec![0u8; self.salt_len]; - openssl::rand::rand_bytes(&mut salt).ok()?; - - Some(salt) - } - #[cfg(all(not(feature = "openssl_sign"), target_arch = "wasm32"))] - { - let salt = crate::wasm::util::get_random_values(self.salt_len).ok()?; - - Some(salt) + Some(crate::wasm::util::get_random_values(self.salt_len).ok()?) } - #[cfg(all(not(feature = "openssl_sign"), not(target_arch = "wasm32")))] + #[cfg(not(target_arch = "wasm32"))] { use rand::prelude::*; diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 3557c9f90..e236fa5f1 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -23,7 +23,7 @@ use std::{ use async_generic::async_generic; use async_recursion::async_recursion; -use c2pa_crypto::{cose::CertificateAcceptancePolicy, hash::sha256}; +use c2pa_crypto::{cose::CertificateTrustPolicy, hash::sha256}; use c2pa_status_tracker::{log_item, DetailedStatusTracker, OneShotStatusTracker, StatusTracker}; use log::error; @@ -80,7 +80,7 @@ pub struct Store { claims: Vec, label: String, provenance_path: Option, - cap: CertificateAcceptancePolicy, + ctp: CertificateTrustPolicy, } struct ManifestInfo<'a> { @@ -124,7 +124,7 @@ impl Store { manifest_box_hash_cache: HashMap::new(), claims: Vec::new(), label: label.to_string(), - cap: CertificateAcceptancePolicy::default(), + ctp: CertificateTrustPolicy::default(), provenance_path: None, //dynamic_assertions: Vec::new(), }; @@ -165,28 +165,28 @@ impl Store { /// Load set of trust anchors used for certificate validation. [u8] containing the /// trust anchors is passed in the trust_vec variable. pub fn add_trust(&mut self, trust_vec: &[u8]) -> Result<()> { - Ok(self.cap.add_trust_anchors(trust_vec)?) + Ok(self.ctp.add_trust_anchors(trust_vec)?) } // Load set of private trust anchors used for certificate validation. [u8] to the /// private trust anchors is passed in the trust_vec variable. This can be called multiple times /// if there are additional trust stores. pub fn add_private_trust_anchors(&mut self, trust_vec: &[u8]) -> Result<()> { - Ok(self.cap.add_trust_anchors(trust_vec)?) + Ok(self.ctp.add_trust_anchors(trust_vec)?) } pub fn add_trust_config(&mut self, trust_vec: &[u8]) -> Result<()> { - self.cap.add_valid_ekus(trust_vec); + self.ctp.add_valid_ekus(trust_vec); Ok(()) } pub fn add_trust_allowed_list(&mut self, allowed_vec: &[u8]) -> Result<()> { - Ok(self.cap.add_end_entity_credentials(allowed_vec)?) + Ok(self.ctp.add_end_entity_credentials(allowed_vec)?) } /// Clear all existing trust anchors pub fn clear_trust_anchors(&mut self) { - self.cap.clear(); + self.ctp.clear(); } /// Get the provenance if available. @@ -453,7 +453,7 @@ impl Store { let data = claim.data().ok()?; let mut validation_log = OneShotStatusTracker::default(); - if let Ok(info) = check_ocsp_status(sig, &data, &self.cap, &mut validation_log) { + if let Ok(info) = check_ocsp_status(sig, &data, &self.ctp, &mut validation_log) { if let Some(revoked_at) = &info.revoked_at { Some(format!( "Certificate Status: Revoked, revoked at: {}", @@ -511,14 +511,14 @@ impl Store { let mut cose_log = OneShotStatusTracker::default(); let result = if _sync { - verify_cose(&sig, &claim_bytes, b"", false, &self.cap, &mut cose_log) + verify_cose(&sig, &claim_bytes, b"", false, &self.ctp, &mut cose_log) } else { verify_cose_async( sig.clone(), claim_bytes, b"".to_vec(), false, - &self.cap, + &self.ctp, &mut cose_log, ) .await @@ -1276,7 +1276,7 @@ impl Store { asset_data, false, check_ingredient_trust, - &store.cap, + &store.ctp, validation_log, )?; @@ -1380,7 +1380,7 @@ impl Store { asset_data, false, check_ingredient_trust, - &store.cap, + &store.ctp, validation_log, ) .await?; @@ -1428,7 +1428,7 @@ impl Store { }; // verify the provenance claim - Claim::verify_claim_async(claim, asset_data, true, true, &store.cap, validation_log) + Claim::verify_claim_async(claim, asset_data, true, true, &store.ctp, validation_log) .await?; Store::ingredient_checks_async(store, claim, asset_data, validation_log).await?; @@ -1458,7 +1458,7 @@ impl Store { }; // verify the provenance claim - Claim::verify_claim(claim, asset_data, true, true, &store.cap, validation_log)?; + Claim::verify_claim(claim, asset_data, true, true, &store.ctp, validation_log)?; Store::ingredient_checks(store, claim, asset_data, validation_log)?; @@ -3883,7 +3883,10 @@ pub mod tests { let r = store.save_to_asset(&ap, &signer, &op); assert!(r.is_err()); - assert_eq!(r.err().unwrap().to_string(), "COSE certificate has expired"); + assert_eq!( + r.err().unwrap().to_string(), + "the certificate was not valid at time of signing" + ); } #[test] diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index 5f3922de4..3e7ca639a 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -20,7 +20,7 @@ use std::{ path::PathBuf, }; -use c2pa_crypto::{cose::CertificateAcceptancePolicy, SigningAlg}; +use c2pa_crypto::{cose::CertificateTrustPolicy, SigningAlg}; use tempfile::TempDir; use crate::{ @@ -219,16 +219,16 @@ pub fn temp_signer_file() -> Box { .expect("get_temp_signer") } -/// Create a [`CertificateAcceptancePolicy`] instance that has the test certificate bundles included. +/// Create a [`CertificateTrustPolicy`] instance that has the test certificate bundles included. /// -/// [`CertificateAcceptancePolicy`]: c2pa_crypto::cose::CertificateAcceptancePolicy -pub fn test_certificate_acceptance_policy() -> CertificateAcceptancePolicy { - let mut cap = CertificateAcceptancePolicy::default(); - cap.add_trust_anchors(include_bytes!( +/// [`CertificateTrustPolicy`]: c2pa_crypto::cose::CertificateTrustPolicy +pub fn test_certificate_acceptance_policy() -> CertificateTrustPolicy { + let mut ctp = CertificateTrustPolicy::default(); + ctp.add_trust_anchors(include_bytes!( "../../tests/fixtures/certs/trust/test_cert_root_bundle.pem" )) .unwrap(); - cap + ctp } #[cfg(feature = "file_io")] diff --git a/sdk/src/wasm/mod.rs b/sdk/src/wasm/mod.rs index 6431d8c9d..2524d8c62 100644 --- a/sdk/src/wasm/mod.rs +++ b/sdk/src/wasm/mod.rs @@ -18,5 +18,3 @@ pub(crate) mod rsa_wasm_signer; pub(crate) use rsa_wasm_signer::RsaWasmSignerAsync; #[cfg(target_arch = "wasm32")] pub(crate) mod util; -#[cfg(target_arch = "wasm32")] -pub(crate) mod webpki_trust_handler; diff --git a/sdk/src/wasm/webpki_trust_handler.rs b/sdk/src/wasm/webpki_trust_handler.rs deleted file mode 100644 index b12efd335..000000000 --- a/sdk/src/wasm/webpki_trust_handler.rs +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2023 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -use asn1_rs::{nom::AsBytes, Any, Class, Header, Tag}; -use c2pa_crypto::{ - cose::CertificateAcceptancePolicy, raw_signature::RawSignatureValidationError, - webcrypto::async_validator_for_signing_alg, SigningAlg, -}; -use x509_parser::{ - der_parser::der::{parse_der_integer, parse_der_sequence_of}, - prelude::*, -}; - -use crate::{cose_validator::*, hash_utils::vec_compare}; - -fn cert_signing_alg(cert: &x509_parser::certificate::X509Certificate) -> Option { - let cert_alg = cert.signature_algorithm.algorithm.clone(); - - let signing_alg = if cert_alg == SHA256_WITH_RSAENCRYPTION_OID { - "rsa256".to_string() - } else if cert_alg == SHA384_WITH_RSAENCRYPTION_OID { - "rsa384".to_string() - } else if cert_alg == SHA512_WITH_RSAENCRYPTION_OID { - "rsa512".to_string() - } else if cert_alg == ECDSA_WITH_SHA256_OID { - SigningAlg::Es256.to_string() - } else if cert_alg == ECDSA_WITH_SHA384_OID { - SigningAlg::Es384.to_string() - } else if cert_alg == ECDSA_WITH_SHA512_OID { - SigningAlg::Es512.to_string() - } else if cert_alg == RSASSA_PSS_OID { - if let Some(parameters) = &cert.signature_algorithm.parameters { - let seq = match parameters.as_sequence() { - Ok(s) => s, - Err(_) => return None, - }; - - let (_i, (ha_alg, mgf_ai)) = match seq.parse(|i| { - let (i, h) =
::from_der(i)?; - if h.class() != Class::ContextSpecific || h.tag() != Tag(0) { - return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); - } - - let (i, ha_alg) = AlgorithmIdentifier::from_der(i) - .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; - - let (i, h) =
::from_der(i)?; - if h.class() != Class::ContextSpecific || h.tag() != Tag(1) { - return Err(nom::Err::Error(asn1_rs::Error::BerValueError)); - } - - let (i, mgf_ai) = AlgorithmIdentifier::from_der(i) - .map_err(|_| nom::Err::Error(asn1_rs::Error::BerValueError))?; - - // Ignore anything that follows these two parameters. - - Ok((i, (ha_alg, mgf_ai))) - }) { - Ok((ii, (h, m))) => (ii, (h, m)), - Err(_) => return None, - }; - - let mgf_ai_parameters = match mgf_ai.parameters { - Some(m) => m, - None => return None, - }; - - let mgf_ai_parameters = match mgf_ai_parameters.as_sequence() { - Ok(m) => m, - Err(_) => return None, - }; - - let (_i, mgf_ai_params_algorithm) = - match ::from_der(&mgf_ai_parameters.content) { - Ok((i, m)) => (i, m), - Err(_) => return None, - }; - - let mgf_ai_params_algorithm = match mgf_ai_params_algorithm.as_oid() { - Ok(m) => m, - Err(_) => return None, - }; - - // must be the same - if ha_alg.algorithm.to_id_string() != mgf_ai_params_algorithm.to_id_string() { - return None; - } - - // check for one of the mandatory types - if ha_alg.algorithm == SHA256_OID { - "ps256".to_string() - } else if ha_alg.algorithm == SHA384_OID { - "ps384".to_string() - } else if ha_alg.algorithm == SHA512_OID { - "ps512".to_string() - } else { - return None; - } - } else { - return None; - } - } else if cert_alg == ED25519_OID { - SigningAlg::Ed25519.to_string() - } else { - return None; - }; - - Some(signing_alg) -} - -async fn verify_data( - cert_der: Vec, - sig_alg: Option, - sig: Vec, - data: Vec, -) -> crate::Result { - use x509_parser::prelude::*; - - let (_, cert) = X509Certificate::from_der(cert_der.as_bytes()) - .map_err(|_e| crate::Error::CoseCertUntrusted)?; - - let certificate_public_key = cert.public_key(); - - let Some(cert_alg_string) = sig_alg else { - return Err(crate::Error::BadParam( - "unknown alg processing cert".to_string(), - )); - }; - - let signing_alg: SigningAlg = cert_alg_string - .parse() - .map_err(|_| crate::Error::UnknownAlgorithm)?; - - // Not sure this is needed any more. Leaving this for now, but I think this should be handled in c2pa_crypto's raw signature code. - let adjusted_sig = if cert_alg_string.starts_with("es") { - match der_to_p1363(&sig, signing_alg) { - Some(p1363) => p1363, - None => sig, - } - } else { - sig - }; - - let Some(validator) = async_validator_for_signing_alg(signing_alg) else { - return Err(crate::Error::UnknownAlgorithm); - }; - - let result = validator - .validate_async(&adjusted_sig, &data, certificate_public_key.raw.as_ref()) - .await; - - match result { - Ok(()) => Ok(true), - Err(RawSignatureValidationError::SignatureMismatch) => Ok(false), - Err(err) => Err(err.into()), - } -} - -// convert der signatures to P1363 format: r | s -fn der_to_p1363(data: &[u8], alg: SigningAlg) -> Option> { - // handle if this is a der sequence - if let Ok((_, bo)) = parse_der_sequence_of(parse_der_integer)(data) { - let seq = bo.as_sequence().ok()?; - - if seq.len() != 2 { - return None; - } - - let rp = seq[0].as_bigint().ok()?; - let sp = seq[1].as_bigint().ok()?; - - let mut r = rp.to_str_radix(16); - let mut s = sp.to_str_radix(16); - - let sig_len: usize = match alg { - SigningAlg::Es256 => 64, - SigningAlg::Es384 => 96, - SigningAlg::Es512 => 132, - _ => return None, - }; - - // pad or truncate as needed - let rp = if r.len() > sig_len { - // truncate - let offset = r.len() - sig_len; - &r[offset..r.len()] - } else { - // pad - while r.len() != sig_len { - r.insert(0, '0'); - } - r.as_ref() - }; - - let sp = if s.len() > sig_len { - // truncate - let offset = s.len() - sig_len; - &s[offset..s.len()] - } else { - // pad - while s.len() != sig_len { - s.insert(0, '0'); - } - s.as_ref() - }; - - if rp.len() != sig_len || rp.len() != sp.len() { - return None; - } - - // merge r and s strings - let mut new_sig = rp.to_string(); - new_sig.push_str(sp); - - // convert back from hex string to byte array - let result = (0..new_sig.len()) - .step_by(2) - .map(|i| { - u8::from_str_radix(&new_sig[i..i + 2], 16) - .map_err(|_err| crate::Error::InvalidEcdsaSignature) - }) - .collect(); - - if let Ok(p1363) = result { - Some(p1363) - } else { - None - } - } else { - Some(data.to_vec()) - } -} - -async fn check_chain_order(certs: &[Vec]) -> crate::Result<()> { - use x509_parser::prelude::*; - - let chain_length = certs.len(); - if chain_length < 2 { - return Ok(()); - } - - for i in 1..chain_length { - let (_, current_cert) = X509Certificate::from_der(&certs[i - 1]) - .map_err(|_e| crate::Error::CoseCertUntrusted)?; - - let issuer_der = certs[i].to_vec(); - let data = current_cert.tbs_certificate.as_ref(); - let sig = current_cert.signature_value.as_ref(); - - let sig_alg = cert_signing_alg(¤t_cert); - - let result = verify_data(issuer_der, sig_alg, sig.to_vec(), data.to_vec()).await; - - // keep going as long as it validate - match result { - Ok(b) => { - if !b { - return Err(crate::Error::OtherError("cert chain order invalid".into())); - } - } - Err(e) => return Err(e), - } - } - Ok(()) -} - -async fn on_trust_list( - cap: &CertificateAcceptancePolicy, - certs: &[Vec], - ee_der: &[u8], -) -> crate::Result { - use x509_parser::prelude::*; - - // check the cert against the allowed list first - if cap.end_entity_cert_ders().any(|cert| cert == ee_der) { - return Ok(true); - } - - // add ee cert if needed to the chain - let full_chain = if !certs.is_empty() && vec_compare(ee_der, &certs[0]) { - certs.to_vec() - } else { - let mut full_chain: Vec> = Vec::new(); - full_chain.push(ee_der.to_vec()); - let mut in_chain = certs.to_vec(); - full_chain.append(&mut in_chain); - full_chain - }; - - // make sure chain is in the correct order and valid - check_chain_order(&full_chain).await?; - - // build anchors and check against trust anchors, - let mut anchors: Vec = Vec::new(); - for anchor_der in cap.trust_anchor_ders() { - let (_, anchor) = - X509Certificate::from_der(anchor_der).map_err(|_e| crate::Error::CoseCertUntrusted)?; - anchors.push(anchor); - } - - if anchors.is_empty() { - return Ok(false); - } - - // work back from last cert in chain against the trust anchors - for cert in certs.iter().rev() { - let (_, chain_cert) = - X509Certificate::from_der(cert).map_err(|_e| crate::Error::CoseCertUntrusted)?; - - for anchor in cap.trust_anchor_ders() { - let data = chain_cert.tbs_certificate.as_ref(); - let sig = chain_cert.signature_value.as_ref(); - - let sig_alg = cert_signing_alg(&chain_cert); - - let (_, anchor_cert) = - X509Certificate::from_der(anchor).map_err(|_e| crate::Error::CoseCertUntrusted)?; - - if chain_cert.issuer() == anchor_cert.subject() { - let result = - verify_data(anchor.clone(), sig_alg, sig.to_vec(), data.to_vec()).await; - - match result { - Ok(b) => { - if b { - return Ok(true); - } - } - Err(_) => continue, - } - } - } - } - // todo: consider (path check and names restrictions) - - Ok(false) -} - -// verify certificate and trust chain -pub(crate) async fn verify_trust_async( - cap: &CertificateAcceptancePolicy, - chain_der: &[Vec], - cert_der: &[u8], - _signing_time_epoc: Option, -) -> crate::Result { - // check configured EKUs against end-entity cert - let Ok((_rem, cert)) = X509Certificate::from_der(cert_der) else { - return Err(crate::Error::CoseCertUntrusted); - }; - - let Ok(Some(eku)) = cert.extended_key_usage() else { - return Err(crate::Error::CoseCertUntrusted); - }; - - let Some(_approved_oid) = cap.has_allowed_eku(&eku.value) else { - return Err(crate::Error::CoseCertUntrusted); - }; - - on_trust_list(cap, chain_der, cert_der).await -} - -#[cfg(test)] -pub mod tests { - #![allow(clippy::expect_used)] - #![allow(clippy::panic)] - #![allow(clippy::unwrap_used)] - - use wasm_bindgen_test::*; - - use super::*; - - #[wasm_bindgen_test] - async fn test_trust_store() { - let cap = crate::utils::test::test_certificate_acceptance_policy(); - - // test all the certs - let ps256 = include_bytes!("../../tests/fixtures/certs/ps256.pub"); - let ps384 = include_bytes!("../../tests/fixtures/certs/ps384.pub"); - let ps512 = include_bytes!("../../tests/fixtures/certs/ps512.pub"); - let es256 = include_bytes!("../../tests/fixtures/certs/es256.pub"); - let es384 = include_bytes!("../../tests/fixtures/certs/es384.pub"); - let es512 = include_bytes!("../../tests/fixtures/certs/es512.pub"); - let ed25519 = include_bytes!("../../tests/fixtures/certs/ed25519.pub"); - - let ps256_certs = load_trust_from_data(ps256).unwrap(); - let ps384_certs = load_trust_from_data(ps384).unwrap(); - let ps512_certs = load_trust_from_data(ps512).unwrap(); - let es256_certs = load_trust_from_data(es256).unwrap(); - let es384_certs = load_trust_from_data(es384).unwrap(); - let es512_certs = load_trust_from_data(es512).unwrap(); - let ed25519_certs = load_trust_from_data(ed25519).unwrap(); - - assert!( - verify_trust_async(&cap, &ps256_certs[1..], &ps256_certs[0], None) - .await - .unwrap() - ); - assert!( - verify_trust_async(&cap, &ps384_certs[1..], &ps384_certs[0], None) - .await - .unwrap() - ); - assert!( - verify_trust_async(&cap, &ps512_certs[1..], &ps512_certs[0], None) - .await - .unwrap() - ); - assert!( - verify_trust_async(&cap, &es256_certs[1..], &es256_certs[0], None) - .await - .unwrap() - ); - - assert!( - verify_trust_async(&cap, &es384_certs[1..], &es384_certs[0], None) - .await - .unwrap() - ); - assert!( - verify_trust_async(&cap, &es512_certs[1..], &es512_certs[0], None) - .await - .unwrap() - ); - - assert!( - verify_trust_async(&cap, &ed25519_certs[1..], &ed25519_certs[0], None) - .await - .unwrap() - ); - } - - #[wasm_bindgen_test] - async fn test_broken_trust_chain() { - let mut cap = CertificateAcceptancePolicy::default(); - - cap.add_trust_anchors(include_bytes!( - "../../tests/fixtures/certs/trust/test_cert_root_bundle.pem" - )) - .unwrap(); - - let ps256 = include_bytes!("../../tests/fixtures/certs/ps256.pub"); - let ps384 = include_bytes!("../../tests/fixtures/certs/ps384.pub"); - let ps512 = include_bytes!("../../tests/fixtures/certs/ps512.pub"); - let es256 = include_bytes!("../../tests/fixtures/certs/es256.pub"); - let es384 = include_bytes!("../../tests/fixtures/certs/es384.pub"); - let es512 = include_bytes!("../../tests/fixtures/certs/es512.pub"); - let ed25519 = include_bytes!("../../tests/fixtures/certs/ed25519.pub"); - - let ps256_certs = load_trust_from_data(ps256).unwrap(); - let ps384_certs = load_trust_from_data(ps384).unwrap(); - let ps512_certs = load_trust_from_data(ps512).unwrap(); - let es256_certs = load_trust_from_data(es256).unwrap(); - let es384_certs = load_trust_from_data(es384).unwrap(); - let es512_certs = load_trust_from_data(es512).unwrap(); - let ed25519_certs = load_trust_from_data(ed25519).unwrap(); - - assert!( - !verify_trust_async(&cap, &ps256_certs[2..], &ps256_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&cap, &ps384_certs[2..], &ps384_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&cap, &ps512_certs[2..], &ps512_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&cap, &es256_certs[2..], &es256_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&cap, &es384_certs[2..], &es384_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&cap, &es512_certs[2..], &es512_certs[0], None) - .await - .unwrap() - ); - assert!( - !verify_trust_async(&cap, &ed25519_certs[2..], &ed25519_certs[0], None) - .await - .unwrap() - ); - } - - // Temporarily moved here because we don't have signing - // implementations for all algorithms on WASM yet. - fn load_trust_from_data(trust_data: &[u8]) -> crate::Result>> { - let mut certs = Vec::new(); - - for pem_result in x509_parser::pem::Pem::iter_from_buffer(trust_data) { - let pem = pem_result.map_err(|_e| crate::Error::CoseInvalidCert)?; - certs.push(pem.contents); - } - Ok(certs) - } -}