From 0afb90bf6453248b3a19cd579f4a21a80981330d Mon Sep 17 00:00:00 2001 From: ok-nick Date: Thu, 5 Jun 2025 12:24:17 -0400 Subject: [PATCH 1/4] feat: impl Soft Binding assertion definition --- sdk/src/assertions/mod.rs | 5 ++ sdk/src/assertions/region_of_interest.rs | 5 +- sdk/src/assertions/soft_binding.rs | 109 +++++++++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 sdk/src/assertions/soft_binding.rs diff --git a/sdk/src/assertions/mod.rs b/sdk/src/assertions/mod.rs index 7809a10e6..38ac51376 100644 --- a/sdk/src/assertions/mod.rs +++ b/sdk/src/assertions/mod.rs @@ -64,3 +64,8 @@ mod uuid_assertion; pub(crate) use uuid_assertion::Uuid; pub mod region_of_interest; + +mod soft_binding; +pub use soft_binding::{ + SoftBinding, SoftBindingBlockMap, SoftBindingScopeMap, SoftBindingTimespanMap, +}; diff --git a/sdk/src/assertions/region_of_interest.rs b/sdk/src/assertions/region_of_interest.rs index 93ae9bc61..e0ab049e4 100644 --- a/sdk/src/assertions/region_of_interest.rs +++ b/sdk/src/assertions/region_of_interest.rs @@ -233,8 +233,9 @@ pub enum Role { /// A region of interest within an asset describing the change. /// -/// This struct can be used from [`Action::changes`][crate::assertions::Action::changes] or -/// [`Metadata::region_of_interest`][crate::assertions::Metadata::region_of_interest]. +/// This struct can be used from [`Action::changes`][crate::assertions::Action::changes], +/// [`Metadata::region_of_interest`][crate::assertions::Metadata::region_of_interest], or +/// [`SoftBindingScopeMap::region`][crate::assertions::soft_binding::SoftBindingScopeMap::region]. #[skip_serializing_none] #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "json_schema", derive(JsonSchema))] diff --git a/sdk/src/assertions/soft_binding.rs b/sdk/src/assertions/soft_binding.rs new file mode 100644 index 000000000..77ba929d9 --- /dev/null +++ b/sdk/src/assertions/soft_binding.rs @@ -0,0 +1,109 @@ +use serde::{Deserialize, Serialize}; + +use super::labels; +use crate::{ + assertion::{Assertion, AssertionBase, AssertionCbor, AssertionJson}, + assertions::region_of_interest::RegionOfInterest, + cbor_types::UriT, + Result, +}; + +const ASSERTION_CREATION_VERSION: usize = 1; + +/// The data structure used to store one or more soft bindings across some or all of the asset's content. +/// +/// https://c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#soft_binding_assertion +#[derive(Serialize, Deserialize, Debug, Default, PartialEq)] +pub struct SoftBinding { + /// A string identifying the soft binding algorithm and version of that algorithm used to compute the value, + /// taken from the [C2PA soft binding algorithm list](https://github.com/c2pa-org/softbinding-algorithm-list). + /// + /// If this field is absent, the algorithm is taken from the `alg_soft` value of the enclosing structure. + /// If both are present, the field in this structure is used. If no value is present in any of these places, + /// this structure is invalid; there is no default. + #[serde(skip_serializing_if = "Option::is_none")] + pub alg: Option, + + /// A list of details about the soft binding. + pub blocks: Vec, + + /// Zero-filled bytes used for filling up space. + #[serde(with = "serde_bytes")] + pub pad: Vec, + + /// Zero-filled bytes used for filling up space. + #[serde(skip_serializing_if = "Option::is_none")] + pub pad2: Option, + + /// A human-readable description of what this hash covers. + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// CBOR byte string describing parameters of the soft binding algorithm. + #[serde(skip_serializing_if = "Option::is_none")] + pub alg_params: Option, + + /// A file or http(s) URL to where the bytes that are being hashed lived. + /// + /// This is useful for cases where the data lives in a different file chunk or side-car + /// than the claim. + #[deprecated] + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, +} + +/// Details about the soft binding, including the referenced value and scope. +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct SoftBindingBlockMap { + /// The scope of the soft binding where it is applicable. + pub scope: SoftBindingScopeMap, + + /// In algorithm specific format, the value of the soft binding computed over this block of digital content. + pub value: String, +} + +/// Soft binding scope, specifying specifically where in an asset the soft binding is applicable. +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct SoftBindingScopeMap { + /// In algorithm specific format, the part of the digital content over which the soft binding value has been computed. + #[deprecated] + #[serde(skip_serializing_if = "Option::is_none")] + pub extent: Option, + + /// For temporal assets, the timespan in which the soft binding is applicable. + #[serde(skip_serializing_if = "Option::is_none")] + pub timespan: Option, + + /// Region of interest in regard to the soft binding. + pub region: RegionOfInterest, +} + +/// Soft binding timespan for temporal assets. +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct SoftBindingTimespanMap { + /// Start of the time range (as milliseconds from media start) over which the soft binding value has been computed. + pub start: u32, + + /// End of the time range (as milliseconds from media start) over which the soft binding value has been computed. + pub end: u32, +} + +impl SoftBinding { + pub const LABEL: &'static str = labels::SOFT_BINDING; +} + +impl AssertionBase for SoftBinding { + const LABEL: &'static str = Self::LABEL; + const VERSION: Option = Some(ASSERTION_CREATION_VERSION); + + fn to_assertion(&self) -> Result { + Self::to_cbor_assertion(self) + } + + fn from_assertion(assertion: &Assertion) -> Result { + Self::from_cbor_assertion(assertion) + } +} + +impl AssertionJson for SoftBinding {} +impl AssertionCbor for SoftBinding {} From d189d865a0007556ba0f3a7700c80fb83629fac3 Mon Sep 17 00:00:00 2001 From: ok-nick Date: Thu, 5 Jun 2025 12:36:15 -0400 Subject: [PATCH 2/4] fix: remove `AssertionJson` impl for `SoftBinding` --- sdk/src/assertions/soft_binding.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/src/assertions/soft_binding.rs b/sdk/src/assertions/soft_binding.rs index 77ba929d9..75ef8a3c8 100644 --- a/sdk/src/assertions/soft_binding.rs +++ b/sdk/src/assertions/soft_binding.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use super::labels; use crate::{ - assertion::{Assertion, AssertionBase, AssertionCbor, AssertionJson}, + assertion::{Assertion, AssertionBase, AssertionCbor}, assertions::region_of_interest::RegionOfInterest, cbor_types::UriT, Result, @@ -105,5 +105,4 @@ impl AssertionBase for SoftBinding { } } -impl AssertionJson for SoftBinding {} impl AssertionCbor for SoftBinding {} From 160426c5380c2bcc03eab3daedc2df7d0ebc4478 Mon Sep 17 00:00:00 2001 From: ok-nick Date: Thu, 5 Jun 2025 12:48:17 -0400 Subject: [PATCH 3/4] fix: use usize for timespan --- sdk/src/assertions/soft_binding.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/assertions/soft_binding.rs b/sdk/src/assertions/soft_binding.rs index 75ef8a3c8..47a640cde 100644 --- a/sdk/src/assertions/soft_binding.rs +++ b/sdk/src/assertions/soft_binding.rs @@ -82,10 +82,10 @@ pub struct SoftBindingScopeMap { #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct SoftBindingTimespanMap { /// Start of the time range (as milliseconds from media start) over which the soft binding value has been computed. - pub start: u32, + pub start: usize, /// End of the time range (as milliseconds from media start) over which the soft binding value has been computed. - pub end: u32, + pub end: usize, } impl SoftBinding { From de7c6d514994b00b9aa2f2a872d68ee8b92798ac Mon Sep 17 00:00:00 2001 From: ok-nick Date: Fri, 6 Jun 2025 09:14:14 -0400 Subject: [PATCH 4/4] docs: clarify `alg_params` field string type --- sdk/src/assertions/soft_binding.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/assertions/soft_binding.rs b/sdk/src/assertions/soft_binding.rs index 47a640cde..fba7b2615 100644 --- a/sdk/src/assertions/soft_binding.rs +++ b/sdk/src/assertions/soft_binding.rs @@ -39,7 +39,7 @@ pub struct SoftBinding { #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, - /// CBOR byte string describing parameters of the soft binding algorithm. + /// A string describing parameters of the soft binding algorithm. #[serde(skip_serializing_if = "Option::is_none")] pub alg_params: Option,