Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
992644d
feat: Add certificate status assertion
alextrnnn Jul 8, 2025
04d984f
feat: Add a way to get OSCP data from asset
alextrnnn Jul 9, 2025
e5e222d
fix: Test verifies round trip, changed visibility of some methods, ad…
alextrnnn Jul 9, 2025
aacd300
feat: Make progress on trying to map OCSP data to certificate serial …
alextrnnn Jul 10, 2025
67531dd
feat: Able to get correct certificate num to OCSP der
alextrnnn Jul 11, 2025
e9076e9
test: Add test showing that map is filled with ocsp, paired with cert…
alextrnnn Jul 11, 2025
8ed22c6
chore: Remove remnant doc
alextrnnn Jul 11, 2025
2c3545a
fix: Modify unit tests to not fetch, certificate statuses can contain…
alextrnnn Jul 15, 2025
1903091
feat: Looks through store validation info to check for ocsp responses…
alextrnnn Jul 15, 2025
7f34a53
fix: Change way to validate ocsp response ders
alextrnnn Jul 16, 2025
716368a
docs: Document assertion struct and some logic
alextrnnn Jul 16, 2025
16a30e1
refactor: Change method params
alextrnnn Jul 16, 2025
fba6710
docs: More docs
alextrnnn Jul 16, 2025
e60ff21
Merge branch 'main' into alextrnnn/certificate-status-assertion
alextrnnn Jul 16, 2025
9e80a1a
fix: Fix merge conflict errors
alextrnnn Jul 16, 2025
f2d8eb9
refactor: Move imports out of function
alextrnnn Jul 16, 2025
8112152
Merge branch 'main' into alextrnnn/certificate-status-assertion
alextrnnn Jul 17, 2025
4506c1f
fix: Remove unnecessary serial number set
alextrnnn Jul 17, 2025
0d34e4e
fix: Add roundtrip tests, add renaming of field
alextrnnn Jul 17, 2025
2426f26
fix: Update image with correctly named assertion, make test consisten…
alextrnnn Jul 17, 2025
82af1f3
chore: Remove old png
alextrnnn Jul 17, 2025
e19859a
Merge branch 'main' into alextrnnn/certificate-status-assertion
alextrnnn Jul 20, 2025
8c6eda1
fix: Update docs, remove non-spec value from validation results, add …
alextrnnn Jul 21, 2025
a26f9c5
chore: Fix clippy warning
alextrnnn Jul 21, 2025
e4c1e63
chore: Remove log item without validation status
alextrnnn Jul 21, 2025
e978dab
Merge branch 'main' into alextrnnn/certificate-status-assertion
alextrnnn Jul 21, 2025
174c71c
test: Add extra check to make sure no empty ocsp_ders were added to map
alextrnnn Jul 21, 2025
acb66fb
Merge branch 'main' into alextrnnn/certificate-status-assertion
alextrnnn Jul 28, 2025
6c815fc
fix: Change to v2 test and lower image size
alextrnnn Jul 29, 2025
313cf0c
chore: Change visibility to crate, remove unused method
alextrnnn Jul 29, 2025
93275a9
chore: Lower size of image further
alextrnnn Jul 29, 2025
acc93b2
Merge branch 'main' into alextrnnn/certificate-status-assertion
alextrnnn Aug 4, 2025
a9cd68d
fix: Merge conflict error
alextrnnn Aug 4, 2025
79dd9ac
feat: Work on integrating usage of certificate status assertion
alextrnnn Aug 5, 2025
2e53688
feat: Add a way to filter only "needed" labels to check ocsp
alextrnnn Aug 6, 2025
ad25d27
fix: Do not add empty assertions
alextrnnn Aug 7, 2025
47394c2
fix: Fix settings
alextrnnn Aug 7, 2025
fd0377a
fix: Override now will look at responses in assertion over responses …
alextrnnn Aug 8, 2025
6586780
fix: Make sure there is only one cert status assertion, give better l…
alextrnnn Aug 11, 2025
b738df7
Merge branch 'main' into alextrnnn/certificate-status-assertion
gpeacock Aug 13, 2025
7e1a09a
test: Update to include unit test
alextrnnn Aug 13, 2025
9d3700b
test: Add checks for validation
alextrnnn Aug 13, 2025
069e3c0
test: Use some existing methods to check validation
alextrnnn Aug 13, 2025
33c7104
format: Fix formatting issues
alextrnnn Aug 13, 2025
5e2d2a1
Merge branch 'main' into alextrnnn/certificate-status-assertion
alextrnnn Aug 13, 2025
405f8f6
format: Fix formatting
alextrnnn Aug 13, 2025
e2d03cb
docs: Add docs
alextrnnn Aug 13, 2025
23a2c60
Merge branch 'main' into alextrnnn/certificate-status-assertion
alextrnnn Aug 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions sdk/src/assertions/certificate_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2025 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 serde::{Deserialize, Serialize};
use serde_bytes::ByteBuf;

use crate::{
assertion::{Assertion, AssertionBase, AssertionCbor},
assertions::labels,
error::Result,
};

/// Helper class to create Certificate Status assertions
#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq)]
pub struct CertificateStatus {
#[serde(rename = "ocspVals")]
pub ocsp_vals: Vec<ByteBuf>,
}

impl CertificateStatus {
/// Label prefix for a [`CertificateStatus`] assertion.
///
/// See <https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#certificate_status_assertion>.
pub const LABEL: &'static str = labels::CERTIFICATE_STATUS;

pub fn new(ocsp_vals: Vec<Vec<u8>>) -> Self {
let mut cs = CertificateStatus {
ocsp_vals: Vec::new(),
};
for oscp_val in ocsp_vals {
cs.ocsp_vals.push(ByteBuf::from(oscp_val));
}
cs
}

pub fn add_ocsp_vals(mut self, ocsp_vals: Vec<Vec<u8>>) -> Self {
for ocsp_val in ocsp_vals {
self.ocsp_vals.push(ByteBuf::from(ocsp_val));
}
self
}
}

impl AsRef<Vec<ByteBuf>> for CertificateStatus {
fn as_ref(&self) -> &Vec<ByteBuf> {
&self.ocsp_vals
}
}

impl AssertionCbor for CertificateStatus {}

impl AssertionBase for CertificateStatus {
const LABEL: &'static str = Self::LABEL;

fn to_assertion(&self) -> Result<Assertion> {
Self::to_cbor_assertion(self)
}

fn from_assertion(assertion: &Assertion) -> Result<Self> {
Self::from_cbor_assertion(assertion)
}
}

#[cfg(test)]
pub mod tests {
#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]

use crate::{assertion::AssertionBase, assertions::CertificateStatus};

#[test]
fn assertions_certificate_status() {
let original = CertificateStatus::new(vec!["ocsp_val".into()]);

assert_eq!(original.ocsp_vals.len(), 1);

let assertion = original.to_assertion().unwrap();
assert_eq!(assertion.mime_type(), "application/cbor");
assert_eq!(assertion.label(), CertificateStatus::LABEL);

let result = CertificateStatus::from_assertion(&assertion).unwrap();
assert_eq!(result, original)
}

#[test]
fn test_json_round_trip() {
let json = serde_json::json!({
"ocspVals" : [
"...",
"..."
]
});

let original: CertificateStatus = serde_json::from_value(json).unwrap();
let assertion = original.to_assertion().unwrap();
let result = CertificateStatus::from_assertion(&assertion).unwrap();

assert_eq!(result, original);
}
}
5 changes: 5 additions & 0 deletions sdk/src/assertions/labels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ pub const CREATIVE_WORK: &str = "stds.schema-org.CreativeWork";
/// See <https://c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#timestamp_assertion>.
pub const TIMESTAMP: &str = "c2pa.time-stamp";

/// Label prefix for a certificate status assertion.
///
/// See <https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#certificate_status_assertion>.
pub const CERTIFICATE_STATUS: &str = "c2pa.certificate-status";

// Assertion store label
pub(crate) const ASSERTION_STORE: &str = "c2pa.assertions";

Expand Down
3 changes: 3 additions & 0 deletions sdk/src/assertions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ pub use box_hash::{BoxHash, BoxMap, C2PA_BOXHASH};
mod data_hash;
pub use data_hash::DataHash;

mod certificate_status;
pub(crate) use certificate_status::CertificateStatus;

mod creative_work;
#[allow(deprecated)]
pub use creative_work::CreativeWork;
Expand Down
48 changes: 45 additions & 3 deletions sdk/src/claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,17 @@ use crate::{
},
asset_io::CAIRead,
cbor_types::{map_cbor_to_type, value_cbor_to_type},
cose_validator::{get_signing_info, get_signing_info_async, verify_cose, verify_cose_async},
cose_validator::{
get_signing_cert_serial_num, get_signing_info, get_signing_info_async, verify_cose,
verify_cose_async,
},
crypto::{
asn1::rfc3161::TstInfo,
base64,
cose::{parse_cose_sign1, CertificateInfo, CertificateTrustPolicy, OcspFetchPolicy},
cose::{
get_ocsp_der, parse_cose_sign1, CertificateInfo, CertificateTrustPolicy,
OcspFetchPolicy,
},
ocsp::OcspResponse,
},
error::{Error, Result},
Expand Down Expand Up @@ -1844,12 +1850,14 @@ impl Claim {
}

let sign1 = parse_cose_sign1(&sig, &data, validation_log)?;
let certificate_serial_num = get_signing_cert_serial_num(&sign1)?.to_string();

// check certificate revocation
check_ocsp_status(
&sign1,
&data,
ctp,
svi.certificate_statuses.get(&certificate_serial_num),
svi.timestamps.get(claim.label()),
validation_log,
)?;
Expand Down Expand Up @@ -1921,11 +1929,13 @@ impl Claim {

let sign1 = parse_cose_sign1(sig, data, validation_log)?;

let certificate_serial_num = get_signing_cert_serial_num(&sign1)?.to_string();
// check certificate revocation
check_ocsp_status(
&sign1,
data,
ctp,
svi.certificate_statuses.get(&certificate_serial_num),
svi.timestamps.get(claim.label()),
validation_log,
)?;
Expand Down Expand Up @@ -3340,6 +3350,14 @@ impl Claim {
self.assertions_by_type(&dummy_timestamp, None)
}

/// Returns list of certificate status assertions.
pub fn certificate_status_assertions(&self) -> Vec<&ClaimAssertion> {
let dummy_data = AssertionData::Cbor(Vec::new());
let dummy_certificate_status =
Assertion::new(assertions::labels::CERTIFICATE_STATUS, None, dummy_data);
self.assertions_by_type(&dummy_certificate_status, None)
}

/// Return list of action assertions.
/// Created assertions have higher priority than gathered assertions
pub fn action_assertions(&self) -> Vec<&ClaimAssertion> {
Expand Down Expand Up @@ -3907,14 +3925,36 @@ impl Claim {
uri
}
}
}

/// Checks whether or not ocsp values are present in claim
pub fn has_ocsp_vals(&self) -> bool {
if !self.certificate_status_assertions().is_empty() {
return false;
}

let data = match self.data() {
Ok(data) => data,
Err(_) => return false,
};

let sig = self.signature_val().clone();
let mut validation_log = StatusTracker::default();

let sign1 = match parse_cose_sign1(&sig, &data, &mut validation_log) {
Ok(sign1) => sign1,
Err(_) => return false,
};

get_ocsp_der(&sign1).is_some()
}
}
#[allow(dead_code)]
#[async_generic]
pub(crate) fn check_ocsp_status(
sign1: &coset::CoseSign1,
data: &[u8],
ctp: &CertificateTrustPolicy,
ocsp_responses: Option<&Vec<Vec<u8>>>,
tst_info: Option<&TstInfo>,
validation_log: &mut StatusTracker,
) -> Result<OcspResponse> {
Expand All @@ -3931,6 +3971,7 @@ pub(crate) fn check_ocsp_status(
data,
fetch_policy,
ctp,
ocsp_responses,
tst_info,
validation_log,
)?)
Expand All @@ -3940,6 +3981,7 @@ pub(crate) fn check_ocsp_status(
data,
fetch_policy,
ctp,
ocsp_responses,
tst_info,
validation_log,
)
Expand Down
9 changes: 9 additions & 0 deletions sdk/src/cose_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use std::{borrow::Cow, io::Write};

use async_generic::async_generic;
use coset::CoseSign1;
use x509_parser::{num_bigint::BigUint, prelude::*};

use crate::{
Expand Down Expand Up @@ -165,6 +166,14 @@ fn extract_serial_from_cert(cert: &X509Certificate) -> BigUint {
cert.serial.clone()
}

/// Returns the unique serial number from the provided CoseSign1
pub(crate) fn get_signing_cert_serial_num(sign1: &CoseSign1) -> Result<BigUint> {
let der_bytes = get_sign_cert(sign1)?;
let (_rem, signcert) =
X509Certificate::from_der(&der_bytes).map_err(|_| Error::CoseInvalidCert)?;
Ok(extract_serial_from_cert(&signcert))
}

#[allow(unused_variables)]
#[async_generic]
pub(crate) fn get_signing_info(
Expand Down
3 changes: 2 additions & 1 deletion sdk/src/crypto/cose/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ mod error;
pub use error::CoseError;

mod ocsp;
pub use ocsp::{check_ocsp_status, check_ocsp_status_async, OcspFetchPolicy};
pub(crate) use ocsp::fetch_and_check_ocsp_response;
pub use ocsp::{check_ocsp_status, check_ocsp_status_async, get_ocsp_der, OcspFetchPolicy};

mod sign;
pub use sign::{sign, sign_async, sign_v2_embedded, sign_v2_embedded_async, CosePayload};
Expand Down
Loading