Skip to content

Commit

Permalink
chore: Reuse HashedUri struct between cawg_identity and c2pa (#841
Browse files Browse the repository at this point in the history
)
  • Loading branch information
scouten-adobe authored Jan 12, 2025
1 parent af8505f commit 86c07e9
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 90 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 2 additions & 35 deletions cawg_identity/src/identity_assertion/signer_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@
// specific language governing permissions and limitations under
// each license.

use std::fmt::{Debug, Formatter};
use std::fmt::Debug;

use c2pa::HashedUri;
use serde::{Deserialize, Serialize};

use crate::internal::debug_byte_slice::DebugByteSlice;

/// A set of _referenced assertions_ and other related data, known overall as
/// the **signer payload.** This binding **SHOULD** generally be construed as
/// authorization of or participation in the creation of the statements
Expand All @@ -37,35 +36,3 @@ pub struct SignerPayload {
// TO DO: Add role and expected_* fields.
// (https://github.com/contentauth/c2pa-rs/issues/816)
}

/// A `HashedUri` provides a reference to content available within the same
/// manifest store.
///
/// This is described in [§8.3, URI References], of the C2PA Technical
/// Specification.
///
/// [§8.3, URI References]: https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_uri_references
#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
pub struct HashedUri {
/// JUMBF URI reference
pub url: String,

/// A string identifying the cryptographic hash algorithm used to compute
/// the hash
#[serde(skip_serializing_if = "Option::is_none")]
pub alg: Option<String>,

/// Byte string containing the hash value
#[serde(with = "serde_bytes")]
pub hash: Vec<u8>,
}

impl Debug for HashedUri {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("HashedUri")
.field("url", &self.url)
.field("alg", &self.alg)
.field("hash", &DebugByteSlice(&self.hash))
.finish()
}
}
2 changes: 2 additions & 0 deletions cawg_identity/src/internal/debug_byte_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// specific language governing permissions and limitations under
// each license.

#![allow(unused)] // TEMPORARY while updating ...

use std::fmt::{Debug, Error, Formatter};

pub(crate) struct DebugByteSlice<'a>(pub(crate) &'a [u8]);
Expand Down
2 changes: 1 addition & 1 deletion cawg_identity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
pub mod builder;

mod identity_assertion;
pub use identity_assertion::signer_payload::{HashedUri, SignerPayload};
pub use identity_assertion::signer_payload::SignerPayload;

pub(crate) mod internal;

Expand Down
39 changes: 0 additions & 39 deletions cawg_identity/src/tests/identity_assertion/hashed_uri.rs

This file was deleted.

1 change: 0 additions & 1 deletion cawg_identity/src/tests/identity_assertion/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@
// specific language governing permissions and limitations under
// each license.

mod hashed_uri;
mod signer_payload;
15 changes: 8 additions & 7 deletions cawg_identity/src/tests/identity_assertion/signer_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,22 @@
// specific language governing permissions and limitations under
// each license.

use c2pa::HashedUri;
use hex_literal::hex;

use crate::{HashedUri, SignerPayload};
use crate::SignerPayload;

#[test]
fn impl_clone() {
// Silly test to ensure code coverage on #[derive] line.

let data_hash_ref = HashedUri::new(
"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data".to_owned(),
Some("sha256".to_owned()),
&hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f"));

let signer_payload = SignerPayload {
referenced_assertions: vec![{
HashedUri {
url: "self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data".to_owned(),
alg: Some("sha256".to_owned()),
hash: hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f").to_vec(), }
}],
referenced_assertions: vec![{ data_hash_ref }],
sig_type: "NONSENSE".to_owned(),
};

Expand Down
3 changes: 2 additions & 1 deletion sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,13 @@ web-sys = { version = "0.3.58", features = [

[dev-dependencies]
anyhow = "1.0.40"
mockall = "0.13.1"
c2pa = { path = ".", default-features = false, features = [
"unstable_api",
] } # allow integration tests to use the new API
glob = "0.3.1"
hex-literal = "0.4.1"
jumbf = "0.4.0"
mockall = "0.13.1"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.31"
Expand Down
76 changes: 70 additions & 6 deletions sdk/src/hashed_uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,32 @@ use std::fmt;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// Hashed Uri structure as defined by C2PA spec
/// It is annotated to produce the correctly tagged cbor serialization
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
use crate::utils::DebugByteSlice;

/// A `HashedUri` provides a reference to content available within the same
/// manifest store.
///
/// This is described in [§8.3, URI References], of the C2PA Technical
/// Specification.
///
/// [§8.3, URI References]: https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_uri_references
#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(feature = "json_schema", derive(JsonSchema))]
pub struct HashedUri {
url: String, // URI stored as tagged cbor
/// JUMBF URI reference
url: String,

/// A string identifying the cryptographic hash algorithm used to compute
/// the hash
#[serde(skip_serializing_if = "Option::is_none")]
alg: Option<String>,

/// Byte string containing the hash value
#[serde(with = "serde_bytes")]
#[cfg_attr(feature = "json_schema", schemars(with = "Vec<u8>"))]
hash: Vec<u8>, // hash stored as cbor byte string
hash: Vec<u8>,

// salt used to generate hash
/// Salt used to generate the hash
#[serde(skip_deserializing, skip_serializing)]
salt: Option<Vec<u8>>,
}
Expand Down Expand Up @@ -73,8 +86,59 @@ impl HashedUri {
}
}

impl fmt::Debug for HashedUri {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_struct("HashedUri")
.field("url", &self.url)
.field("alg", &self.alg)
.field("hash", &DebugByteSlice(&self.hash))
.finish()
}
}

impl fmt::Display for HashedUri {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "url: {}, alg: {:?}, hash", self.url, self.alg)
}
}

#[cfg(test)]
mod tests {
use hex_literal::hex;

use super::HashedUri;

#[test]
fn impl_clone() {
let h = HashedUri::new(
"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data".to_owned(),
Some("sha256".to_owned()),
&hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f"),
);

let h2 = h.clone();
assert!(h == h2);
}

#[test]
fn impl_debug() {
let h = HashedUri::new(
"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data".to_owned(),
Some("sha256".to_owned()),
&hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f"),
);

assert_eq!(format!("{h:#?}"), "HashedUri {\n url: \"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data\",\n alg: Some(\n \"sha256\",\n ),\n hash: 32 bytes starting with [53, d1, b2, cf, 4e, 6d, 9a, 97, ed, 92, 81, 18, 3f, a5, d8, 36, c3, 27, 51, b9],\n}");
}

#[test]
fn impl_display() {
let h = HashedUri::new(
"self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data".to_owned(),
Some("sha256".to_owned()),
&hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f"),
);

assert_eq!(format!("{h}"), "url: self#jumbf=c2pa/urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4/c2pa.assertions/c2pa.hash.data, alg: Some(\"sha256\"), hash");
}
}
1 change: 1 addition & 0 deletions sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub use dynamic_assertion::{DynamicAssertion, PreliminaryClaim};
pub use error::{Error, Result};
pub use external_manifest::ManifestPatchCallback;
pub use hash_utils::{hash_stream_by_alg, HashRange};
pub use hashed_uri::HashedUri;
pub use ingredient::Ingredient;
#[cfg(feature = "file_io")]
pub use ingredient::{DefaultOptions, IngredientOptions};
Expand Down
31 changes: 31 additions & 0 deletions sdk/src/utils/debug_byte_slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2024 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 std::fmt::{Debug, Error, Formatter};

pub(crate) struct DebugByteSlice<'a>(pub(crate) &'a [u8]);

impl Debug for DebugByteSlice<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
if self.0.len() > 20 {
write!(
f,
"{} bytes starting with {:02x?}",
self.0.len(),
&self.0[0..20]
)
} else {
write!(f, "{:02x?}", self.0)
}
}
}
4 changes: 4 additions & 0 deletions sdk/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
// each license.

pub(crate) mod cbor_types;

mod debug_byte_slice;
pub(crate) use debug_byte_slice::DebugByteSlice;

#[allow(dead_code)]
pub(crate) mod hash_utils;
pub(crate) mod io_utils;
Expand Down

0 comments on commit 86c07e9

Please sign in to comment.