Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 15 additions & 1 deletion sdk/src/assertions/labels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,25 @@ pub(crate) const DATABOX_STORE: &str = "c2pa.databoxes";
/// See <https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_asset_reference>.
pub const ASSET_REFERENCE: &str = "c2pa.asset-ref";

/// Label prefix for a metadata assertion.
/// Label prefix for a C2PA metadata assertion.
///
/// A C2PA metadata assertion can only be used for [specific metadata fields]
/// as described in the C2PA Technical Specification and only if those fields
/// are generated from a hardware or software source.
///
/// See <https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_metadata>.
///
/// [specific metadata fields]: https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#metadata_annex
pub const METADATA: &str = "c2pa.metadata";

/// Label prefix for a [CAWG metadata assertion].
///
/// The CAWG metadata assertion is intended for human-generated metadata
/// and may contain metadata from any documented schema.
///
/// [CAWG metadata assertion]: https://cawg.io/metadata/
pub const CAWG_METADATA: &str = "cawg.metadata";

/// Must have a label that ends in '.metadata' and is preceded by an entity-specific namespace.
/// For example, a 'com.litware.metadata' assertion would be valid.
pub static METADATA_LABEL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Expand Down
37 changes: 30 additions & 7 deletions sdk/src/assertions/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,13 @@ pub mod tests {
#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]

use crate::{assertion::AssertionBase, assertions::metadata::Metadata};
use crate::{
assertion::AssertionBase,
assertions::{
labels::{CAWG_METADATA, METADATA},
metadata::Metadata,
},
};

const SPEC_EXAMPLE: &str = r#"{
"@context" : {
Expand Down Expand Up @@ -485,6 +491,17 @@ pub mod tests {
"exifEX:LensSpecification": { "@list": [ 1.55, 4.2, 1.6, 2.4 ] }
}"#;

const CAWG_METADATA_EXAMPLE: &str = r#" {
"@context" : {
"dc" : "http://purl.org/dc/elements/1.1/"
},
"dc:created": "2025 August 13",
"dc:creator": [
"John Doe"
]
}
"#;

const CUSTOM_METADATA: &str = r#" {
"@context" : {
"bar": "http://foo.com/bar/1.0/"
Expand Down Expand Up @@ -522,13 +539,13 @@ pub mod tests {

#[test]
fn metadata_from_json() {
let metadata = Metadata::new("c2pa.metadata", SPEC_EXAMPLE).unwrap();
let metadata = Metadata::new(METADATA, SPEC_EXAMPLE).unwrap();
assert!(metadata.is_valid());
}

#[test]
fn assertion_round_trip() {
let metadata = Metadata::new("c2pa.metadata", SPEC_EXAMPLE).unwrap();
let metadata = Metadata::new(METADATA, SPEC_EXAMPLE).unwrap();
let assertion = metadata.to_assertion().unwrap();
let result = Metadata::from_assertion(&assertion).unwrap();
assert_eq!(metadata, result);
Expand All @@ -539,21 +556,27 @@ pub mod tests {
let mut metadata = Metadata::new("custom.metadata", CUSTOM_METADATA).unwrap();
assert!(metadata.is_valid());
// c2pa.metadata has restrictions on fields
metadata.label = "c2pa.metadata".to_owned();
metadata.label = METADATA.to_owned();
assert!(!metadata.is_valid());
}

#[test]
fn test_cawg_metadata() {
let metadata = Metadata::new(CAWG_METADATA, CAWG_METADATA_EXAMPLE).unwrap();
assert!(metadata.is_valid());
}

#[test]
fn test_field_not_in_context() {
let mut metadata = Metadata::new("custom.metadata", MISSING_CONTEXT).unwrap();
assert!(!metadata.is_valid());
metadata.label = "c2pa.metadata".to_owned();
metadata.label = METADATA.to_owned();
assert!(!metadata.is_valid());
}

#[test]
fn test_uri_is_not_allowed() {
let mut metadata = Metadata::new("c2pa.metadata", MISMATCH_URI).unwrap();
let mut metadata = Metadata::new(METADATA, MISMATCH_URI).unwrap();
assert!(!metadata.is_valid());
// custom metadata does not have restriction on uris
metadata.label = "custom.metadata".to_owned();
Expand All @@ -562,7 +585,7 @@ pub mod tests {

#[test]
fn test_empty_context() {
let metadata = Metadata::new("c2pa.metadata", EMPTY_CONTEXT).unwrap();
let metadata = Metadata::new(METADATA, EMPTY_CONTEXT).unwrap();
assert!(!metadata.is_valid());
}
}
11 changes: 1 addition & 10 deletions sdk/tests/test_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,8 @@ fn test_builder_embedded_v1_otgp() -> Result<()> {
#[test]
fn test_dynamic_assertions_builder() -> Result<()> {
use c2pa::{
// assertions::{CreativeWork, SchemaDotOrgPerson},
dynamic_assertion::{DynamicAssertion, DynamicAssertionContent, PartialClaim},
Signer,
SigningAlg,
Signer, SigningAlg,
};
use serde::Serialize;
#[derive(Serialize)]
Expand All @@ -220,17 +218,13 @@ fn test_dynamic_assertions_builder() -> Result<()> {

impl DynamicAssertion for TestDynamicAssertion {
fn label(&self) -> String {
//CreativeWork::LABEL.to_string()
"com.mycompany.myassertion".to_string()
}

fn reserve_size(&self) -> Result<usize> {
let assertion = TestAssertion {
my_tag: "some value I will replace".to_string(),
};
// let assertion = CreativeWork::new()
// .add_author(SchemaDotOrgPerson::new().set_name("me").unwrap())
// .unwrap();
Ok(serde_json::to_string(&assertion)?.len())
}

Expand All @@ -247,9 +241,6 @@ fn test_dynamic_assertions_builder() -> Result<()> {
})
.any(|a| a.url().contains("c2pa.hash")));

// let assertion =
// CreativeWork::new().add_author(SchemaDotOrgPerson::new().set_name("me")?)?;

let assertion = TestAssertion {
my_tag: "some value I will replace".to_string(),
};
Expand Down