c2pa-rs

A fork of https://github.com/contentauth/c2pa-rs/
git clone git://archive.git.mtrnord.blog/mtrnords-photography-manager/c2pa-rs.git
Log | Files | Refs | README

commit 59a0636f6a7f4f87f3f13c35e4441c57687c0b17
parent 27aae35b0e4fde7f70b8c9d97ee8fcd572ca14d5
Author: Dylan ross <dylan.ssor@gmail.com>
Date:   Mon, 12 Jun 2023 12:59:05 -0700

includes the cert serial number in the ValidationInfo output (#263)

creates CertInfo struct containing subject and serial_number of cert.

Co-authored-by: Dylan Ross <dyross@adobe.com>
Diffstat:
Msdk/src/claim.rs | 9++++++++-
Msdk/src/cose_validator.rs | 57+++++++++++++++++++++++++++++++++++++++++++++++----------
Msdk/src/manifest.rs | 19+++++++++++++++++--
Msdk/src/validator.rs | 2++
4 files changed, 74 insertions(+), 13 deletions(-)

diff --git a/sdk/src/claim.rs b/sdk/src/claim.rs @@ -981,7 +981,7 @@ impl Claim { } } - /// Return the signing date and time for this claim, if there is one. + /// Return the signing issuer for this claim, if there is one. pub fn signing_issuer(&self) -> Option<String> { if let Some(validation_data) = self.signature_info() { validation_data.issuer_org @@ -990,6 +990,13 @@ impl Claim { } } + /// Return the cert's serial number, if there is one. + pub fn signing_cert_serial(&self) -> Option<String> { + self.signature_info() + .and_then(|validation_info| validation_info.cert_serial_number) + .map(|serial| serial.to_string()) + } + /// Return information about the signature pub fn signature_info(&self) -> Option<ValidationInfo> { let sig = self.signature_val(); diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs @@ -20,6 +20,7 @@ use coset::{ }; use x509_parser::{ der_parser::{ber::parse_ber_sequence, oid}, + num_bigint::BigUint, oid_registry::Oid, prelude::*, }; @@ -708,6 +709,14 @@ fn get_timestamp_info(sign1: &coset::CoseSign1, data: &[u8]) -> Result<TstInfo> Err(Error::NotFound) } +/// A wrapper containing information of the signing cert. +pub(crate) struct CertInfo { + /// The name of the identity the certificate is issued to. + pub subject: String, + /// The serial number of the cert. Will be unique to the CA. + pub serial_number: BigUint, +} + fn extract_subject_from_cert(cert: &X509Certificate) -> Result<String> { cert.subject() .iter_organization() @@ -718,6 +727,11 @@ fn extract_subject_from_cert(cert: &X509Certificate) -> Result<String> { .map_err(|_e| Error::CoseX5ChainMissing) } +/// Returns the unique serial number from the provided cert. +fn extract_serial_from_cert(cert: &X509Certificate) -> BigUint { + cert.serial.clone() +} + /// Asynchronously validate a COSE_SIGN1 byte vector and verify against expected data /// cose_bytes - byte array containing the raw COSE_SIGN1 data /// data: data that was used to create the cose_bytes, these must match @@ -808,8 +822,13 @@ pub async fn verify_cose_async( sign1.payload.as_ref().unwrap_or(&vec![]), ); // get "to be signed" bytes - if let Ok(issuer) = validate_with_cert_async(alg, &sign1.signature, &tbs, &der_bytes).await { - result.issuer_org = Some(issuer); + if let Ok(CertInfo { + subject, + serial_number, + }) = validate_with_cert_async(alg, &sign1.signature, &tbs, &der_bytes).await + { + result.issuer_org = Some(subject); + result.cert_serial_number = Some(serial_number); result.validated = true; result.alg = Some(alg); @@ -832,6 +851,7 @@ pub fn get_signing_info( let mut date = None; let mut issuer_org = None; let mut alg: Option<SigningAlg> = None; + let mut cert_serial_number = None; let sign1 = get_cose_sign1(cose_bytes, data, validation_log).and_then(|sign1| { // get the public key der @@ -840,6 +860,7 @@ pub fn get_signing_info( let _ = X509Certificate::from_der(&der_bytes).map(|(_rem, signcert)| { date = get_signing_time(&sign1, data); issuer_org = extract_subject_from_cert(&signcert).ok(); + cert_serial_number = Some(extract_serial_from_cert(&signcert)); if let Ok(a) = get_signing_alg(&sign1) { alg = Some(a); } @@ -858,6 +879,7 @@ pub fn get_signing_info( alg, validated: false, cert_chain: Vec::new(), + cert_serial_number, } } #[cfg(not(target_arch = "wasm32"))] @@ -876,6 +898,7 @@ pub fn get_signing_info( alg, validated: false, cert_chain: certs, + cert_serial_number, } } } @@ -962,8 +985,13 @@ pub fn verify_cose( // Check the signature, which needs to have the same `additional_data` provided, by // providing a closure that can do the verify operation. sign1.verify_signature(additional_data, |sig, verify_data| -> Result<()> { - if let Ok(issuer) = validate_with_cert(validator, sig, verify_data, der_bytes) { - result.issuer_org = Some(issuer); + if let Ok(CertInfo { + subject, + serial_number, + }) = validate_with_cert(validator, sig, verify_data, der_bytes) + { + result.issuer_org = Some(subject); + result.cert_serial_number = Some(serial_number); result.validated = true; result.alg = Some(alg); @@ -997,7 +1025,7 @@ fn validate_with_cert( sig: &[u8], data: &[u8], der_bytes: &[u8], -) -> Result<String> { +) -> Result<CertInfo> { // get the cert in der format let (_rem, signcert) = X509Certificate::from_der(der_bytes).map_err(|_err| Error::CoseInvalidCert)?; @@ -1005,7 +1033,10 @@ fn validate_with_cert( let pk_der = pk.raw; if validator.validate(sig, data, pk_der)? { - Ok(extract_subject_from_cert(&signcert).unwrap_or_default()) + Ok(CertInfo { + subject: extract_subject_from_cert(&signcert).unwrap_or_default(), + serial_number: extract_serial_from_cert(&signcert), + }) } else { Err(Error::CoseSignature) } @@ -1017,14 +1048,17 @@ async fn validate_with_cert_async( sig: &[u8], data: &[u8], der_bytes: &[u8], -) -> Result<String> { +) -> Result<CertInfo> { let (_rem, signcert) = X509Certificate::from_der(der_bytes).map_err(|_err| Error::CoseMissingKey)?; let pk = signcert.public_key(); let pk_der = pk.raw; if validate_async(signing_alg, sig, data, pk_der).await? { - Ok(extract_subject_from_cert(&signcert).unwrap_or_default()) + Ok(CertInfo { + subject: extract_subject_from_cert(&signcert).unwrap_or_default(), + serial_number: extract_serial_from_cert(&signcert), + }) } else { Err(Error::CoseSignature) } @@ -1036,7 +1070,7 @@ async fn validate_with_cert_async( sig: &[u8], data: &[u8], der_bytes: &[u8], -) -> Result<String> { +) -> Result<CertInfo> { // get the cert in der format let (_rem, signcert) = X509Certificate::from_der(der_bytes).map_err(|_err| Error::CoseInvalidCert)?; @@ -1046,7 +1080,10 @@ async fn validate_with_cert_async( let validator = get_validator(signing_alg); if validator.validate(sig, data, pk_der)? { - Ok(extract_subject_from_cert(&signcert).unwrap_or_default()) + Ok(CertInfo { + subject: extract_subject_from_cert(&signcert).unwrap_or_default(), + serial_number: extract_serial_from_cert(&signcert), + }) } else { Err(Error::CoseSignature) } diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs @@ -451,10 +451,16 @@ impl Manifest { } /// Sets the signature information for the report - fn set_signature(&mut self, issuer: Option<&String>, time: Option<&String>) -> &mut Self { + fn set_signature( + &mut self, + issuer: Option<&String>, + time: Option<&String>, + cert_serial: Option<&String>, + ) -> &mut Self { self.signature_info = Some(SignatureInfo { issuer: issuer.cloned(), time: time.cloned(), + cert_serial_number: cert_serial.cloned(), }); self } @@ -675,7 +681,11 @@ impl Manifest { "added signature issuer={:?} time={:?}", issuer, signing_time ); - manifest.set_signature(issuer.as_ref(), signing_time.as_ref()); + manifest.set_signature( + issuer.as_ref(), + signing_time.as_ref(), + claim.signing_cert_serial().as_ref(), + ); } Ok(manifest) @@ -1158,6 +1168,11 @@ pub struct SignatureInfo { /// human readable issuing authority for this signature #[serde(skip_serializing_if = "Option::is_none")] issuer: Option<String>, + + /// The serial number of the certificate + #[serde(skip_serializing_if = "Option::is_none")] + cert_serial_number: Option<String>, + /// the time the signature was created #[serde(skip_serializing_if = "Option::is_none")] time: Option<String>, diff --git a/sdk/src/validator.rs b/sdk/src/validator.rs @@ -12,6 +12,7 @@ // each license. use chrono::{DateTime, Utc}; +use x509_parser::num_bigint::BigUint; #[cfg(feature = "openssl_sign")] use crate::openssl::{EcValidator, EdValidator, RsaValidator}; @@ -21,6 +22,7 @@ use crate::{Result, SigningAlg}; pub struct ValidationInfo { pub alg: Option<SigningAlg>, // validation algorithm pub date: Option<DateTime<Utc>>, + pub cert_serial_number: Option<BigUint>, pub issuer_org: Option<String>, pub validated: bool, // claim signature is valid pub cert_chain: Vec<u8>, // certificate chain used to validate signature