diff --git a/sdk/src/assertions/mod.rs b/sdk/src/assertions/mod.rs index b9dc41787..74cf4809e 100644 --- a/sdk/src/assertions/mod.rs +++ b/sdk/src/assertions/mod.rs @@ -67,3 +67,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..fba7b2615 --- /dev/null +++ b/sdk/src/assertions/soft_binding.rs @@ -0,0 +1,108 @@ +use serde::{Deserialize, Serialize}; + +use super::labels; +use crate::{ + assertion::{Assertion, AssertionBase, AssertionCbor}, + 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, + + /// A 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: usize, + + /// End of the time range (as milliseconds from media start) over which the soft binding value has been computed. + pub end: usize, +} + +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 AssertionCbor for SoftBinding {}