From d2031e217fc7411942a0b59b6b519efeeb7dd957 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Tue, 7 Jan 2025 13:18:18 -0800 Subject: [PATCH 1/3] feat(cawg_identity): Add `SignerPayload` struct --- .commitlintrc.yml | 3 +- Cargo.lock | 13 ++++ cawg_identity/Cargo.toml | 14 +++- cawg_identity/src/identity_assertion/mod.rs | 14 ++++ .../src/identity_assertion/signer_payload.rs | 66 +++++++++++++++++++ .../src/internal/debug_byte_slice.rs | 31 +++++++++ cawg_identity/src/internal/mod.rs | 14 ++++ cawg_identity/src/lib.rs | 33 +++++++--- .../tests/identity_assertion/hashed_uri.rs | 38 +++++++++++ .../src/tests/identity_assertion/mod.rs | 15 +++++ .../identity_assertion/signer_payload.rs | 33 ++++++++++ .../src/tests/internal/debug_byte_slice.rs | 37 +++++++++++ cawg_identity/src/tests/internal/mod.rs | 14 ++++ cawg_identity/src/tests/mod.rs | 25 +++++++ 14 files changed, 340 insertions(+), 10 deletions(-) create mode 100644 cawg_identity/src/identity_assertion/mod.rs create mode 100644 cawg_identity/src/identity_assertion/signer_payload.rs create mode 100644 cawg_identity/src/internal/debug_byte_slice.rs create mode 100644 cawg_identity/src/internal/mod.rs create mode 100644 cawg_identity/src/tests/identity_assertion/hashed_uri.rs create mode 100644 cawg_identity/src/tests/identity_assertion/mod.rs create mode 100644 cawg_identity/src/tests/identity_assertion/signer_payload.rs create mode 100644 cawg_identity/src/tests/internal/debug_byte_slice.rs create mode 100644 cawg_identity/src/tests/internal/mod.rs create mode 100644 cawg_identity/src/tests/mod.rs diff --git a/.commitlintrc.yml b/.commitlintrc.yml index 09c1eb3e1..75f333748 100644 --- a/.commitlintrc.yml +++ b/.commitlintrc.yml @@ -21,10 +21,11 @@ rules: scope: level: error options: + - c2patool + - cawg_identity - export_schema - make_test_images - sdk - - c2patool # Scope may be empty # (NOTE: Disabled for now while we work around diff --git a/Cargo.lock b/Cargo.lock index e5d7b5502..f824b7013 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -822,6 +822,13 @@ dependencies = [ [[package]] name = "cawg-identity" version = "0.1.1" +dependencies = [ + "hex-literal", + "serde", + "serde_bytes", + "wasm-bindgen", + "wasm-bindgen-test", +] [[package]] name = "cc" @@ -1801,6 +1808,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hkdf" version = "0.12.4" diff --git a/cawg_identity/Cargo.toml b/cawg_identity/Cargo.toml index 86c6999d3..48d049f17 100644 --- a/cawg_identity/Cargo.toml +++ b/cawg_identity/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cawg-identity" version = "0.1.1" -description = "Implementation of CAWG identity assertion specification" +description = "Rust SDK for CAWG (Creator Assertions Working Group) identity assertion" authors = [ "Eric Scouten ", ] @@ -21,3 +21,15 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +hex-literal = "0.4.1" +serde = { version = "1.0.197", features = ["derive"] } +serde_bytes = "0.11.14" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2.95" + +[dev-dependencies] +serde = { version = "1.0.197", features = ["derive"] } + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "0.3.31" diff --git a/cawg_identity/src/identity_assertion/mod.rs b/cawg_identity/src/identity_assertion/mod.rs new file mode 100644 index 000000000..6068fd7a5 --- /dev/null +++ b/cawg_identity/src/identity_assertion/mod.rs @@ -0,0 +1,14 @@ +// 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. + +pub(crate) mod signer_payload; diff --git a/cawg_identity/src/identity_assertion/signer_payload.rs b/cawg_identity/src/identity_assertion/signer_payload.rs new file mode 100644 index 000000000..a170e5f2a --- /dev/null +++ b/cawg_identity/src/identity_assertion/signer_payload.rs @@ -0,0 +1,66 @@ +// 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, Formatter}; + +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 described by those assertions and corresponding portions of the C2PA asset in which they appear. +/// +/// This is described in [§5.1, Overview], of the CAWG Identity Assertion specification. +/// +/// [§5.1, Overview]: https://cawg.io/identity/1.1-draft/#_overview +#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)] +pub struct SignerPayload { + /// List of assertions referenced by this credential signature + pub referenced_assertions: Vec, + + /// A string identifying the data type of the `signature` field + pub sig_type: String, + // 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, + + /// Byte string containing the hash value + #[serde(with = "serde_bytes")] + pub hash: Vec, +} + +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() + } +} diff --git a/cawg_identity/src/internal/debug_byte_slice.rs b/cawg_identity/src/internal/debug_byte_slice.rs new file mode 100644 index 000000000..446989937 --- /dev/null +++ b/cawg_identity/src/internal/debug_byte_slice.rs @@ -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<'a> Debug for DebugByteSlice<'a> { + 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) + } + } +} diff --git a/cawg_identity/src/internal/mod.rs b/cawg_identity/src/internal/mod.rs new file mode 100644 index 000000000..3aa3c775c --- /dev/null +++ b/cawg_identity/src/internal/mod.rs @@ -0,0 +1,14 @@ +// 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. + +pub(crate) mod debug_byte_slice; diff --git a/cawg_identity/src/lib.rs b/cawg_identity/src/lib.rs index 6131d268c..fad44a640 100644 --- a/cawg_identity/src/lib.rs +++ b/cawg_identity/src/lib.rs @@ -1,10 +1,27 @@ -/// This crate is a placeholder. More to come soon ... -pub fn does_nothing_yet() {} +// 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. + +#![deny(clippy::expect_used)] +#![deny(clippy::panic)] +#![deny(clippy::unwrap_used)] +#![deny(missing_docs)] +#![deny(warnings)] +#![doc = include_str!("../README.md")] + +mod identity_assertion; +pub use identity_assertion::signer_payload::{HashedUri, SignerPayload}; + +pub(crate) mod internal; #[cfg(test)] -mod tests { - #[test] - fn it_does_nothing() { - println!("Placeholder"); - } -} +pub(crate) mod tests; diff --git a/cawg_identity/src/tests/identity_assertion/hashed_uri.rs b/cawg_identity/src/tests/identity_assertion/hashed_uri.rs new file mode 100644 index 000000000..4ecd2c91b --- /dev/null +++ b/cawg_identity/src/tests/identity_assertion/hashed_uri.rs @@ -0,0 +1,38 @@ +// 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 hex_literal::hex; + +use crate::HashedUri; + +#[test] +fn impl_clone() { + let h = 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(), + }; + + assert_eq!(h.clone(), h); +} + +#[test] +fn impl_debug() { + let h = 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(), + }; + + 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}"); +} diff --git a/cawg_identity/src/tests/identity_assertion/mod.rs b/cawg_identity/src/tests/identity_assertion/mod.rs new file mode 100644 index 000000000..09f80c1a5 --- /dev/null +++ b/cawg_identity/src/tests/identity_assertion/mod.rs @@ -0,0 +1,15 @@ +// 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. + +mod hashed_uri; +mod signer_payload; diff --git a/cawg_identity/src/tests/identity_assertion/signer_payload.rs b/cawg_identity/src/tests/identity_assertion/signer_payload.rs new file mode 100644 index 000000000..0e3fb66b6 --- /dev/null +++ b/cawg_identity/src/tests/identity_assertion/signer_payload.rs @@ -0,0 +1,33 @@ +// 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 hex_literal::hex; + +use crate::{HashedUri, SignerPayload}; + +#[test] +fn impl_clone() { + // Silly test to ensure code coverage on #[derive] line. + + 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(), } + }], + sig_type: "NONSENSE".to_owned(), + }; + + assert_eq!(signer_payload, signer_payload.clone()); +} diff --git a/cawg_identity/src/tests/internal/debug_byte_slice.rs b/cawg_identity/src/tests/internal/debug_byte_slice.rs new file mode 100644 index 000000000..6806abdd7 --- /dev/null +++ b/cawg_identity/src/tests/internal/debug_byte_slice.rs @@ -0,0 +1,37 @@ +// 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 hex_literal::hex; + +use crate::internal::debug_byte_slice::*; + +#[test] +fn debug_byte_slice() { + let h = hex!("01020354595f"); + let s = DebugByteSlice(&h); + assert_eq!(format!("{s:#?}"), "[01, 02, 03, 54, 59, 5f]"); + + let h = hex!("000102030405060708090a0b0c0d0e0f10111213"); + let s = DebugByteSlice(&h); + assert_eq!( + format!("{s:#?}"), + "[00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13]" + ); + + let h = hex!("000102030405060708090a0b0c0d0e0f1011121314"); + let s = DebugByteSlice(&h); + assert_eq!( + format!("{s:#?}"), + "21 bytes starting with [00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13]" + ); +} diff --git a/cawg_identity/src/tests/internal/mod.rs b/cawg_identity/src/tests/internal/mod.rs new file mode 100644 index 000000000..df2cce3ad --- /dev/null +++ b/cawg_identity/src/tests/internal/mod.rs @@ -0,0 +1,14 @@ +// 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. + +mod debug_byte_slice; diff --git a/cawg_identity/src/tests/mod.rs b/cawg_identity/src/tests/mod.rs new file mode 100644 index 000000000..ad1e07c0f --- /dev/null +++ b/cawg_identity/src/tests/mod.rs @@ -0,0 +1,25 @@ +// 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. + +// Tests are grouped under this module so as to avoid +// having the test code itself included in coverage numbers. + +#![allow(clippy::expect_used)] +#![allow(clippy::panic)] +#![allow(clippy::unwrap_used)] + +mod identity_assertion; +mod internal; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); From 1c9409fde9384bdf1b8976d4803ac718559014d9 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Tue, 7 Jan 2025 15:41:31 -0800 Subject: [PATCH 2/3] Clippy --- cawg_identity/src/internal/debug_byte_slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cawg_identity/src/internal/debug_byte_slice.rs b/cawg_identity/src/internal/debug_byte_slice.rs index 446989937..a29624166 100644 --- a/cawg_identity/src/internal/debug_byte_slice.rs +++ b/cawg_identity/src/internal/debug_byte_slice.rs @@ -15,7 +15,7 @@ use std::fmt::{Debug, Error, Formatter}; pub(crate) struct DebugByteSlice<'a>(pub(crate) &'a [u8]); -impl<'a> Debug for DebugByteSlice<'a> { +impl Debug for DebugByteSlice<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { if self.0.len() > 20 { write!( From 89b8325b99117e04b76e0986f40321ac28476c98 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Tue, 7 Jan 2025 15:46:04 -0800 Subject: [PATCH 3/3] Attempt to fix coverage gap --- cawg_identity/src/tests/identity_assertion/hashed_uri.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cawg_identity/src/tests/identity_assertion/hashed_uri.rs b/cawg_identity/src/tests/identity_assertion/hashed_uri.rs index 4ecd2c91b..52be81e07 100644 --- a/cawg_identity/src/tests/identity_assertion/hashed_uri.rs +++ b/cawg_identity/src/tests/identity_assertion/hashed_uri.rs @@ -23,7 +23,8 @@ fn impl_clone() { hash: hex!("53d1b2cf4e6d9a97ed9281183fa5d836c32751b9d2fca724b40836befee7d67f").to_vec(), }; - assert_eq!(h.clone(), h); + let h2 = h.clone(); + assert!(h == h2); } #[test]