diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16f7b8d95..cb5c9b4d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,27 +146,31 @@ jobs: command: fmt args: --all -- --check - doc_format: - name: Doc format + docs_rs: + name: Preflight docs.rs build runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - - name: Install Rust toolchain + - name: Install nightly Rust toolchain + # Nightly is used here because the docs.rs build + # uses nightly and we use doc_cfg features that are + # not in stable Rust as of this writing (Rust 1.62). uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: nightly override: true - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v1 - - name: Run cargo docs - uses: actions-rs/cargo@v1 - with: - command: doc - args: --no-deps + # This is intended to mimic the docs.rs build + # environment. The goal is to fail PR validation + # if the subsequent release would result in a failed + # documentation build on docs.rs. + run: cargo +nightly doc --all-features + env: + RUSTDOCFLAGS: --cfg docsrs + DOCS_RS: 1 cargo-deny: name: License / vulnerability audit @@ -179,7 +183,7 @@ jobs: - advisories - bans licenses sources - # Prevent sudden announcement of a new advisory from failing ci: + # Prevent sudden announcement of a new advisory from failing CI: continue-on-error: ${{ matrix.checks == 'advisories' }} steps: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7a4b87f1f..c83fb73d2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,6 +5,7 @@ on: jobs: publish: + concurrency: publish-mutex runs-on: ubuntu-latest steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index eeb51fd93..6abe7b34f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,29 @@ This project adheres to [Semantic Versioning](https://semver.org), except that Do not manually edit this file. It will be automatically updated when a new release is published. +## 0.8.1 +_15 July 2022_ + +* Use rsa crate for RSA-PSS verification in Wasm ([#77](https://github.com/contentauth/c2pa-rs/pull/77)) + +## 0.8.0 +_15 July 2022_ + +* Add a new API to provide access to COSE signing logic for external signers ([#75](https://github.com/contentauth/c2pa-rs/pull/75)) +* (MINOR) Move crate-level functions for creating signers to new public `create_signer` mod ([#72](https://github.com/contentauth/c2pa-rs/pull/72)) + +## 0.7.2 +_14 July 2022_ + +* Fix broken documentation build ([#74](https://github.com/contentauth/c2pa-rs/pull/74)) + +## 0.7.1 +_14 July 2022_ + +* Configure docs.rs to include feature-gated items ([#73](https://github.com/contentauth/c2pa-rs/pull/73)) +* Update XMP Toolkit to 0.5.0 ([#71](https://github.com/contentauth/c2pa-rs/pull/71)) +* Refactor code to limit memory usage and remove data copies during hash generation ([#67](https://github.com/contentauth/c2pa-rs/pull/67)) + ## 0.7.0 _29 June 2022_ diff --git a/README.md b/README.md index 72cfa1d9c..d0f95ba60 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -c2pa = "0.7.0" +c2pa = "0.8.1" ``` ## Crate features @@ -52,7 +52,7 @@ c2pa = "0.7.0" * `bmff` enables handling of BMFF file formats. Currently only MP4, M4A, and MOV are enabled for writing. * `file_io` enables manifest generation, signing via OpenSSL, and embedding manifests in various file formats. * `serialize_thumbnails` includes binary thumbnail data in the [Serde](https://serde.rs/) serialization output. -* `xmp_write` enables updating XMP on embed with the `dcterms:provenance` field (requires [xmp_toolkit](https://crates.io/crates/xmp_toolkit)). +* `xmp_write` enables updating XMP on embed with the `dcterms:provenance` field. (Requires [xmp_toolkit](https://crates.io/crates/xmp_toolkit).) ## Rust version requirements diff --git a/make_test_images/Cargo.toml b/make_test_images/Cargo.toml index 1f0227c3b..d39f81355 100644 --- a/make_test_images/Cargo.toml +++ b/make_test_images/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "make_test_images" -version = "0.7.0" +version = "0.8.1" authors = ["Gavin Peacock "] license = "MIT OR Apache-2.0" edition = "2018" diff --git a/make_test_images/src/make_test_images.rs b/make_test_images/src/make_test_images.rs index e541b835b..7e3cd29c6 100644 --- a/make_test_images/src/make_test_images.rs +++ b/make_test_images/src/make_test_images.rs @@ -12,11 +12,9 @@ // each license. //! Constructs a set of test images using a configuration script -//! use c2pa::{ assertions::{c2pa_action, Action, Actions, CreativeWork, SchemaDotOrgPerson}, - get_signer_from_files, jumbf_io, Error, Ingredient, IngredientOptions, Manifest, ManifestStore, - Signer, + create_signer, jumbf_io, Error, Ingredient, IngredientOptions, Manifest, ManifestStore, Signer, }; use anyhow::{Context, Result}; @@ -38,7 +36,7 @@ fn get_signer_with_alg(alg: &str) -> c2pa::Result> { signcert_path.push(format!("../sdk/tests/fixtures/certs/{}.pub", alg)); let mut pkey_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); pkey_path.push(format!("../sdk/tests/fixtures/certs/{}.pem", alg)); - get_signer_from_files(signcert_path, pkey_path, alg, None) + create_signer::from_files(signcert_path, pkey_path, alg, None) } /// Defines an operation for creating a test image diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 128334784..b0aa8fbb7 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "c2pa" -version = "0.7.0" +version = "0.8.1" description = "Rust SDK for C2PA (Coalition for Content Provenance and Authenticity) implementors" authors = ["Maurice Fisher ", "Gavin Peacock ", "Eric Scouten ", "Leonard Rosenthol ", "Dave Kozma "] license = "MIT OR Apache-2.0" @@ -12,6 +12,10 @@ edition = "2018" rust-version = "1.58.0" exclude = ["tests/fixtures"] +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + [features] async_signer = ["async-trait", "file_io"] bmff = [] # Work in progress support for BMFF-based containers @@ -55,7 +59,7 @@ serde_cbor = "0.11.1" serde_derive = "1.0.127" serde_json = "1.0.66" serde-transcode = "1.1.1" -sha2 = "0.9.5" +sha2 = "0.10.2" tempfile = "3.1.0" thiserror = ">= 1.0.20, < 1.0.32" time = ">= 0.2.23" @@ -70,25 +74,28 @@ url = "2.2.2" ureq = "2.4.0" instant = "0.1.0" openssl = { version = "0.10.31", features = ["vendored"], optional = true } -xmp_toolkit = { version = "0.3.4", optional = true } +xmp_toolkit = { version = "0.5.1", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] console_log = { version = "0.2", features = ["color"] } -getrandom = { version = "0.2.2", features = ["js"] } +getrandom = { version = "0.2.7", features = ["js"] } # We need to use the `inaccurate` flag here to ensure usage of the JavaScript Date API # to handle certificate timestamp checking correctly. -instant = { version = "0.1.0", features = ["wasm-bindgen", "inaccurate"] } -js-sys = "0.3.54" -serde-wasm-bindgen = "0.4.1" -wasm-bindgen = "0.2.77" -wasm-bindgen-futures = "0.4.27" -web-sys = { version = "0.3.54", features = ["Crypto", "SubtleCrypto", "CryptoKey", "Window", "WorkerGlobalScope"] } +instant = { version = "0.1.12", features = ["wasm-bindgen", "inaccurate"] } +js-sys = "0.3.58" +rand = "0.8.5" +rsa = "0.6.1" +serde-wasm-bindgen = "0.4.3" +spki = "0.6.0" +wasm-bindgen = "0.2.81" +wasm-bindgen-futures = "0.4.31" +web-sys = { version = "0.3.58", features = ["Crypto", "SubtleCrypto", "CryptoKey", "Window", "WorkerGlobalScope"] } [dev-dependencies] anyhow = "1.0.40" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = "0.3.0" +wasm-bindgen-test = "0.3.31" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] actix = "0.11.0" diff --git a/sdk/examples/client/client.rs b/sdk/examples/client/client.rs index 48a706c48..dd529005b 100644 --- a/sdk/examples/client/client.rs +++ b/sdk/examples/client/client.rs @@ -17,7 +17,7 @@ use anyhow::Result; use c2pa::{ assertions::{c2pa_action, labels, Action, Actions, CreativeWork, SchemaDotOrgPerson}, - get_signer_from_files, Ingredient, Manifest, ManifestStore, + create_signer, Ingredient, Manifest, ManifestStore, }; use std::path::PathBuf; @@ -111,7 +111,7 @@ pub fn main() -> Result<()> { // sign and embed into the target file let signcert_path = "../sdk/tests/fixtures/certs.ps256.pem"; let pkey_path = "../sdk/tests/fixtures/certs.ps256.pub"; - let signer = get_signer_from_files(signcert_path, pkey_path, "ps256", None)?; + let signer = create_signer::from_files(signcert_path, pkey_path, "ps256", None)?; manifest.embed(&source, &dest, &*signer)?; diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index 16ecf9d77..5d061bc60 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -11,14 +11,22 @@ // specific language governing permissions and limitations under // each license. -use crate::time_stamp::{cose_timestamp_countersign, make_cose_timestamp}; -use crate::{Error, Result, Signer}; // enable when TimeStamp Authority is ready +//! Provides access to COSE signature generation. + +#![deny(missing_docs)] use ciborium::value::Value; use coset::{ iana, CoseSign1, CoseSign1Builder, Header, HeaderBuilder, Label, TaggedCborSerializable, }; +use crate::{ + claim::Claim, + cose_validator::verify_cose, + status_tracker::OneShotStatusTracker, + time_stamp::{cose_timestamp_countersign, make_cose_timestamp}, + Error, Result, Signer, +}; fn build_unprotected_header( data: &[u8], alg: &str, @@ -36,23 +44,6 @@ fn build_unprotected_header( "ps512" => HeaderBuilder::new() .algorithm(iana::Algorithm::PS512) .build(), - /* No longer supported by C2PA - "rs256" => { - HeaderBuilder::new() - .algorithm(iana::Algorithm::RS256) - .build() - } - "rs384" => { - HeaderBuilder::new() - .algorithm(iana::Algorithm::RS384) - .build() - } - "rs512" => { - HeaderBuilder::new() - .algorithm(iana::Algorithm::RS512) - .build() - } - */ "es256" => HeaderBuilder::new() .algorithm(iana::Algorithm::ES256) .build(), @@ -68,7 +59,6 @@ fn build_unprotected_header( _ => return Err(Error::UnsupportedType), }; - // Get the public CAs for the Signer let sc_der_array_or_bytes = match certs.len() { 1 => Value::Bytes(certs[0].clone()), // single cert _ => { @@ -109,8 +99,43 @@ fn build_unprotected_header( Ok((alg_id, unprotected_header)) } -/// Returns signed Cose_Sign1 bytes for "data". The Cose_Sign1 will be signed with the algorithm from `Signer`. -pub fn cose_sign(signer: &dyn Signer, data: &[u8], box_size: usize) -> Result> { +/// Generate a COSE signature for a block of bytes which must be a valid C2PA +/// claim structure. +/// +/// Should only be used when the underlying signature mechanism is detached +/// from the generation of the C2PA manifest (and thus the claim embedded in it). +/// +/// ## Actions taken +/// +/// 1. Verifies that the data supplied is a valid C2PA claim. The function will +/// respond with [`Error::ClaimDecoding`] if not. +/// 2. Signs the data using the provided [`Signer`] instance. Will ensure that +/// the signature is padded to match `box_size`, which should be the number of +/// bytes reserved for the `c2pa.signature` JUMBF box in this claim's manifest. +/// (If `box_size` is too small for the generated signature, this function +/// will respond with an error.) +/// 3. Verifies that the signature is valid COSE. Will respond with an error +/// if unable to validate. +pub fn sign_claim(claim_bytes: &[u8], signer: &dyn Signer, box_size: usize) -> Result> { + // must be a valid Claim + let label = "dummy_label"; + let _claim = Claim::from_data(label, claim_bytes)?; + + // generate and verify a CoseSign1 representation of the data + cose_sign(signer, claim_bytes, box_size).and_then(|sig| { + // Sanity check: Ensure that this signature is valid. + + let mut cose_log = OneShotStatusTracker::new(); + match verify_cose(&sig, claim_bytes, b"", false, &mut cose_log) { + Ok(_) => Ok(sig), + Err(err) => Err(err), + } + }) +} + +/// Returns signed Cose_Sign1 bytes for `data`. +/// The Cose_Sign1 will be signed with the algorithm from [`Signer`]. +pub(crate) fn cose_sign(signer: &dyn Signer, data: &[u8], box_size: usize) -> Result> { // 13.2.1. X.509 Certificates // // X.509 Certificates are stored in a header named x5chain draft-ietf-cose-x509. @@ -300,3 +325,28 @@ fn pad_cose_sig(sign1: &mut CoseSign1, end_size: usize) -> Result> { )); pad_cose_sig(sign1, end_size) } + +#[cfg(test)] +mod tests { + #![allow(clippy::unwrap_used)] + + use super::sign_claim; + + use crate::{claim::Claim, utils::test::temp_signer}; + + #[test] + fn test_sign_claim() { + let mut claim = Claim::new("extern_sign_test", Some("contentauth")); + claim.build().unwrap(); + + let claim_bytes = claim.data().unwrap(); + + let box_size = 10000; + + let signer = temp_signer(); + + let cose_sign1 = sign_claim(&claim_bytes, &signer, box_size).unwrap(); + + assert_eq!(cose_sign1.len(), box_size); + } +} diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index e3b00109a..0261c0ccd 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -1013,9 +1013,9 @@ pub mod tests { fn test_verify_cose_good() { let validator = get_validator("ps256").unwrap(); - let sig_bytes = include_bytes!("../tests/fixtures/sig.data"); - let data_bytes = include_bytes!("../tests/fixtures/data.data"); - let key_bytes = include_bytes!("../tests/fixtures/key.data"); + let sig_bytes = include_bytes!("../tests/fixtures/sig_ps256.data"); + let data_bytes = include_bytes!("../tests/fixtures/data_ps256.data"); + let key_bytes = include_bytes!("../tests/fixtures/key_ps256.data"); assert!(validator .validate(sig_bytes, data_bytes, key_bytes) @@ -1050,9 +1050,9 @@ pub mod tests { fn test_verify_cose_bad() { let validator = get_validator("ps256").unwrap(); - let sig_bytes = include_bytes!("../tests/fixtures/sig.data"); - let data_bytes = include_bytes!("../tests/fixtures/data.data"); - let key_bytes = include_bytes!("../tests/fixtures/key.data"); + let sig_bytes = include_bytes!("../tests/fixtures/sig_ps256.data"); + let data_bytes = include_bytes!("../tests/fixtures/data_ps256.data"); + let key_bytes = include_bytes!("../tests/fixtures/key_ps256.data"); let mut bad_bytes = data_bytes.to_vec(); bad_bytes[0] = b'c'; diff --git a/sdk/src/create_signer.rs b/sdk/src/create_signer.rs new file mode 100644 index 000000000..280dfbe67 --- /dev/null +++ b/sdk/src/create_signer.rs @@ -0,0 +1,109 @@ +// Copyright 2022 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(missing_docs)] + +//! The `create_signer` module provides a way to obtain a [`Signer`] +//! instance for each signing format supported by this crate. + +use std::path::Path; + +use crate::{ + error::{Error, Result}, + openssl::{EcSigner, EdSigner, RsaSigner}, + signer::ConfigurableSigner, + Signer, +}; + +/// Creates a [`Signer`] instance using signing certificate and private key +/// as byte slices. +/// +/// The signing certificate and private key are passed to the underlying +/// C++ code, which copies them into its own storage. +/// +/// # Arguments +/// +/// * `signcert` - Signing certificate +/// * `pkey` - Private key +/// * `alg` - Format for signing. Must be one of the supported +/// formats (`rs256`, `rs384`, `rs512`, `ps256`, `ps384`, `ps512`, +/// `es256`, `es384`, `es512`, or `ed25519`). +/// * `tsa_url` - Optional URL for a timestamp authority +pub fn from_keys( + signcert: &[u8], + pkey: &[u8], + alg: &str, + tsa_url: Option, +) -> Result> { + Ok(match alg { + "ps256" | "ps384" | "ps512" => Box::new(RsaSigner::from_signcert_and_pkey( + signcert, + pkey, + alg.to_owned(), + tsa_url, + )?), + "es256" | "es384" | "es512" => Box::new(EcSigner::from_signcert_and_pkey( + signcert, + pkey, + alg.to_owned(), + tsa_url, + )?), + "ed25519" => Box::new(EdSigner::from_signcert_and_pkey( + signcert, + pkey, + alg.to_owned(), + tsa_url, + )?), + _ => return Err(Error::BadParam(alg.to_owned())), + }) +} + +/// Creates a [`Signer`] instance using signing certificate and +/// private key files. +/// +/// # Arguments +/// +/// * `signcert_path` - Path to the signing certificate file +/// * `pkey_path` - Path to the private key file +/// * `alg` - Format for signing. Must be one of the supported +/// formats (`rs256`, `rs384`, `rs512`, `ps256`, `ps384`, `ps512`, +/// `es256`, `es384`, `es512`, or `ed25519`). +/// * `tsa_url` - Optional URL for a timestamp authority +pub fn from_files>( + signcert_path: P, + pkey_path: P, + alg: &str, + tsa_url: Option, +) -> Result> { + Ok(match alg { + "ps256" | "ps384" | "ps512" => Box::new(RsaSigner::from_files( + &signcert_path, + &pkey_path, + alg.to_owned(), + tsa_url, + )?), + "es256" | "es384" | "es512" => Box::new(EcSigner::from_files( + &signcert_path, + &pkey_path, + alg.to_owned(), + tsa_url, + )?), + "ed25519" => Box::new(EdSigner::from_files( + &signcert_path, + &pkey_path, + alg.to_owned(), + tsa_url, + )?), + _ => return Err(Error::BadParam(alg.to_owned())), + }) +} diff --git a/sdk/src/embedded_xmp.rs b/sdk/src/embedded_xmp.rs index b7e9dd145..087e64b9c 100644 --- a/sdk/src/embedded_xmp.rs +++ b/sdk/src/embedded_xmp.rs @@ -13,7 +13,10 @@ use std::path::Path; -use xmp_toolkit::{OpenFileOptions, XmpFile, XmpFileError, XmpMeta}; +use log::error; +use xmp_toolkit::{OpenFileOptions, XmpError, XmpFile, XmpMeta}; + +use crate::{Error, Result}; /// Add the URI for the active manifest to the XMP packet for a file. /// @@ -22,20 +25,29 @@ use xmp_toolkit::{OpenFileOptions, XmpFile, XmpFileError, XmpMeta}; /// /// This does not check the claim at all; it is presumed /// that the string that is passed is a valid signed claim. -pub(crate) fn add_manifest_uri_to_file>( - path: P, - manifest_uri: &str, -) -> Result<(), XmpFileError> { - XmpMeta::register_namespace("http://purl.org/dc/terms/", "dcterms"); +pub(crate) fn add_manifest_uri_to_file>(path: P, manifest_uri: &str) -> Result<()> { + XmpMeta::register_namespace("http://purl.org/dc/terms/", "dcterms").map_err(xmp_write_err)?; + + let mut f = XmpFile::new().map_err(xmp_write_err)?; + + f.open_file(path, OpenFileOptions::default().for_update()) + .map_err(xmp_write_err)?; - let mut f = XmpFile::new(); + let mut m = match f.xmp() { + Some(m) => m, + None => XmpMeta::new().map_err(xmp_write_err)?, + }; - f.open_file(path, OpenFileOptions::OPEN_FOR_UPDATE)?; + m.set_property("http://purl.org/dc/terms/", "provenance", manifest_uri) + .map_err(xmp_write_err)?; - let mut m = f.xmp().unwrap_or_else(XmpMeta::new); - m.set_property("http://purl.org/dc/terms/", "provenance", manifest_uri); - f.put_xmp(&m); + f.put_xmp(&m).map_err(xmp_write_err)?; f.close(); Ok(()) } + +fn xmp_write_err(err: XmpError) -> crate::Error { + error!("Unable to add manifest URI to file: {:?}", err); + Error::XmpWriteError +} diff --git a/sdk/src/error.rs b/sdk/src/error.rs index 2188a5047..302c76d60 100644 --- a/sdk/src/error.rs +++ b/sdk/src/error.rs @@ -136,6 +136,12 @@ pub enum Error { #[error("WASM verifier error")] WasmVerifier, + #[error("WASM RSA-PSS key import error: {0}")] + WasmRsaKeyImport(String), + + #[error("WASM RSA-PSS verification error")] + WasmRsaVerification, + #[error("WASM crypto key error")] WasmKey, diff --git a/sdk/src/ingredient.rs b/sdk/src/ingredient.rs index a6413a568..6c369dc11 100644 --- a/sdk/src/ingredient.rs +++ b/sdk/src/ingredient.rs @@ -233,7 +233,8 @@ impl Ingredient { /// Identifies this ingredient as the parent. /// - /// Only one ingredient can be flagged as a parent. + /// Only one ingredient should be flagged as a parent. + /// Use Manifest.set_parent to ensure this is the only parent ingredient pub fn set_is_parent(&mut self) -> &mut Self { self.is_parent = Some(true); self @@ -251,6 +252,15 @@ impl Ingredient { self } + /// Adds a [ValidationStatus] to this ingredient. + pub fn add_validation_status(&mut self, status: ValidationStatus) -> &mut Self { + match &mut self.validation_status { + None => self.validation_status = Some(vec![status]), + Some(validation_status) => validation_status.push(status), + } + self + } + /// Adds any desired [`Metadata`] to this ingredient. pub fn set_metadata(&mut self, metadata: Metadata) -> &mut Self { self.metadata = Some(metadata); @@ -651,33 +661,13 @@ pub struct IngredientOptions { } #[cfg(test)] -#[cfg(feature = "file_io")] mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] use super::*; - use crate::{assertions::Metadata, utils::test::fixture_path}; - - const MANIFEST_JPEG: &str = "C.jpg"; - const BAD_SIGNATURE_JPEG: &str = "E-sig-CA.jpg"; - const PRERELEASE_JPEG: &str = "prerelease.jpg"; - - fn stats(ingredient: &Ingredient) -> usize { - let thumb_size = ingredient.thumbnail().map_or(0, |(_, image)| image.len()); - let manifest_data_size = ingredient.manifest_data().map_or(0, |v| v.len()); - - println!( - " {} instance_id: {}, thumb size: {}, manifest_data size: {}", - ingredient.title(), - ingredient.instance_id(), - thumb_size, - manifest_data_size, - ); - ingredient.title().len() + ingredient.instance_id().len() + thumb_size + manifest_data_size - } - + use crate::assertions::Metadata; #[test] fn test_ingredient_api() { let mut ingredient = Ingredient::new("title", "format", "instance_id"); @@ -690,12 +680,14 @@ mod tests { .set_metadata(Metadata::new()) .set_thumbnail("format", "thumbnail".as_bytes().to_vec()) .set_active_manifest("active_manifest") - .set_manifest_data("data".as_bytes().to_vec()); + .set_manifest_data("data".as_bytes().to_vec()) + .add_validation_status(ValidationStatus::new("status_code")); assert_eq!(ingredient.title(), "title2"); assert_eq!(ingredient.format(), "format"); assert_eq!(ingredient.instance_id(), "instance_id"); assert_eq!(ingredient.document_id(), Some("document_id")); assert_eq!(ingredient.provenance(), Some("provenance")); + assert_eq!(ingredient.hash(), Some("hash")); assert!(ingredient.is_parent()); assert!(ingredient.metadata().is_some()); assert_eq!( @@ -704,9 +696,43 @@ mod tests { ); assert_eq!(ingredient.active_manifest(), Some("active_manifest")); assert_eq!(ingredient.manifest_data(), Some("data".as_bytes())); + assert_eq!( + ingredient.validation_status().unwrap()[0].code(), + "status_code" + ); + } +} + +#[cfg(test)] +#[cfg(feature = "file_io")] +mod tests_file_io { + #![allow(clippy::expect_used)] + #![allow(clippy::unwrap_used)] + + use super::*; + + use crate::utils::test::fixture_path; + + const MANIFEST_JPEG: &str = "C.jpg"; + const BAD_SIGNATURE_JPEG: &str = "E-sig-CA.jpg"; + const PRERELEASE_JPEG: &str = "prerelease.jpg"; + + fn stats(ingredient: &Ingredient) -> usize { + let thumb_size = ingredient.thumbnail().map_or(0, |(_, image)| image.len()); + let manifest_data_size = ingredient.manifest_data().map_or(0, |v| v.len()); + + println!( + " {} instance_id: {}, thumb size: {}, manifest_data size: {}", + ingredient.title(), + ingredient.instance_id(), + thumb_size, + manifest_data_size, + ); + ingredient.title().len() + ingredient.instance_id().len() + thumb_size + manifest_data_size } #[test] + #[cfg(feature = "file_io")] fn test_psd() { // std::env::set_var("RUST_LOG", "debug"); // env_logger::init(); @@ -722,6 +748,7 @@ mod tests { } #[test] + #[cfg(feature = "file_io")] fn test_jpg() { let ap = fixture_path(MANIFEST_JPEG); let ingredient = Ingredient::from_file(&ap).expect("from_file"); @@ -737,6 +764,7 @@ mod tests { } #[test] + #[cfg(feature = "file_io")] fn test_jpg_options() { let options = IngredientOptions { make_hash: true, @@ -758,6 +786,7 @@ mod tests { } #[test] + #[cfg(feature = "file_io")] fn test_png_no_claim() { let ap = fixture_path("libpng-test.png"); let ingredient = Ingredient::from_file(&ap).expect("from_file"); @@ -792,6 +821,7 @@ mod tests { } #[test] + #[cfg(feature = "file_io")] fn test_jpg_prerelease() { let ap = fixture_path(PRERELEASE_JPEG); let ingredient = Ingredient::from_file(&ap).expect("from_file"); @@ -811,6 +841,7 @@ mod tests { } #[test] + #[cfg(feature = "file_io")] fn test_jpg_nested() { let ap = fixture_path("CIE-sig-CA.jpg"); let ingredient = Ingredient::from_file(&ap).expect("from_file"); diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 10c242010..40c9303a2 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -15,6 +15,7 @@ #![deny(clippy::expect_used)] #![deny(clippy::panic)] #![deny(clippy::unwrap_used)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg, doc_cfg_hide))] //! This library supports reading, creating and embedding C2PA data //! with JPEG and PNG images. @@ -45,7 +46,7 @@ //! # use c2pa::Result; //! use c2pa::{ //! assertions::User, -//! get_signer_from_files, +//! create_signer, //! Manifest //! }; //! @@ -63,7 +64,7 @@ //! // Create a ps256 signer using certs and key files //! let signcert_path = "tests/fixtures/certs/ps256.pub"; //! let pkey_path = "tests/fixtures/certs/ps256.pem"; -//! let signer = get_signer_from_files(signcert_path, pkey_path, "ps256", None)?; +//! let signer = create_signer::from_files(signcert_path, pkey_path, "ps256", None)?; //! //! // embed a manifest using the signer //! manifest.embed(&source, &dest, &*signer)?; @@ -76,6 +77,9 @@ pub mod assertions; mod cose_validator; +#[cfg(feature = "file_io")] +pub mod create_signer; + mod error; pub use error::{Error, Result}; @@ -97,8 +101,6 @@ pub use manifest_store_report::ManifestStoreReport; pub(crate) mod ocsp_utils; #[cfg(feature = "file_io")] mod openssl; -#[cfg(feature = "file_io")] -pub use crate::openssl::signer::{get_signer, get_signer_from_files}; #[cfg(feature = "file_io")] mod signer; @@ -113,8 +115,10 @@ pub(crate) mod assertion; pub(crate) mod asset_handlers; pub(crate) mod asset_io; pub(crate) mod claim; + #[cfg(feature = "file_io")] -pub(crate) mod cose_sign; +pub mod cose_sign; + #[cfg(all(feature = "xmp_write", feature = "file_io"))] pub(crate) mod embedded_xmp; pub(crate) mod hashed_uri; diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 4c5dc6f82..b35a9435f 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -567,7 +567,7 @@ impl Manifest { /// # use c2pa::Result; /// use c2pa::{ /// assertions::User, - /// get_signer_from_files, + /// create_signer, /// Manifest /// }; /// # fn main() -> Result<()> { @@ -580,7 +580,7 @@ impl Manifest { /// // Create a PS256 signer using certs and public key files. /// let signcert_path = "tests/fixtures/certs/ps256.pub"; /// let pkey_path = "tests/fixtures/certs/ps256.pem"; - /// let signer = get_signer_from_files(signcert_path, pkey_path, "ps256", None)?; + /// let signer = create_signer::from_files(signcert_path, pkey_path, "ps256", None)?; /// /// // Embed a manifest using the signer. /// manifest.embed(&source, &dest, &*signer)?; diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index d630f6082..66f40323e 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -263,6 +263,28 @@ mod tests { assert!(manifest.time().is_some()); } + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[ignore] + #[allow(dead_code)] + async fn manifest_report_image_wasm() { + let image_bytes = include_bytes!("../tests/fixtures/CA.jpg"); + + let manifest_store = + ManifestStore::from_bytes_async("image/jpeg", image_bytes.to_vec(), true) + .await + .unwrap(); + + assert!(!manifest_store.manifests.is_empty()); + assert!(manifest_store.active_label().is_some()); + assert!(manifest_store.get_active().is_some()); + assert!(!manifest_store.manifests().is_empty()); + assert!(manifest_store.validation_status().is_none()); + let manifest = manifest_store.get_active().unwrap(); + assert!(!manifest.ingredients().is_empty()); + assert_eq!(manifest.issuer().unwrap(), "C2PA Test Signing Cert"); + assert!(manifest.time().is_some()); + } + #[test] #[cfg(feature = "file_io")] fn manifest_report_from_file() { diff --git a/sdk/src/openssl/mod.rs b/sdk/src/openssl/mod.rs index 51ebd6540..1d28f4c38 100644 --- a/sdk/src/openssl/mod.rs +++ b/sdk/src/openssl/mod.rs @@ -29,7 +29,6 @@ pub(crate) use ed_signer::EdSigner; mod ed_validator; pub(crate) use ed_validator::EdValidator; -pub mod signer; #[cfg(test)] pub(crate) mod temp_signer; diff --git a/sdk/src/openssl/signer.rs b/sdk/src/openssl/signer.rs deleted file mode 100644 index a540e76a4..000000000 --- a/sdk/src/openssl/signer.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::path::Path; - -use crate::{ - error::{Error, Result}, - openssl::{EcSigner, EdSigner, RsaSigner}, - signer::ConfigurableSigner, - Signer, -}; - -/// Creates a signer using signcert and public key -/// -/// Can generate a [`Signer`] instance for all supported formats. -/// -/// # Arguments -/// -/// * `signcert` - A buffer containing a signcert -/// * `pkey` - A buffer containing a public key file -/// * `alg` - A format for signing. Must be one of (`rs256`, `rs384`, `rs512`, -/// `ps256`, `ps384`, `ps512`, `es256`, `es384`, `es512`, or `ed25519`). -/// * `tsa_url` - Optional URL for a timestamp authority. -/// -/// # Returns -/// -/// Returns a [`Signer`] instance or Error - -pub fn get_signer( - signcert: &[u8], - pkey: &[u8], - alg: &str, - tsa_url: Option, -) -> Result> { - Ok(match alg { - "ps256" | "ps384" | "ps512" => Box::new(RsaSigner::from_signcert_and_pkey( - signcert, - pkey, - alg.to_owned(), - tsa_url, - )?), - "es256" | "es384" | "es512" => Box::new(EcSigner::from_signcert_and_pkey( - signcert, - pkey, - alg.to_owned(), - tsa_url, - )?), - "ed25519" => Box::new(EdSigner::from_signcert_and_pkey( - signcert, - pkey, - alg.to_owned(), - tsa_url, - )?), - _ => return Err(Error::BadParam(alg.to_owned())), - }) -} - -/// Creates a signer using signcert and public key files -/// -/// Can generate a [`Signer`] instance for all supported formats. -/// -/// # Arguments -/// -/// * `signcert_path` - A path to the signing cert file -/// * `pkey_path` - A path to the public key file -/// * `alg` - A format for signing. Must be one of (`rs256`, `rs384`, `rs512`, -/// `ps256`, `ps384`, `ps512`, `es256`, `es384`, `es512`, or `ed25519`). -/// * `tsa_url` - Optional URL for a timestamp authority. -/// -/// # Returns -/// -/// Returns a [`Signer`] instance or Error - -pub fn get_signer_from_files>( - signcert_path: P, - pkey_path: P, - alg: &str, - tsa_url: Option, -) -> Result> { - Ok(match alg { - "ps256" | "ps384" | "ps512" => Box::new(RsaSigner::from_files( - &signcert_path, - &pkey_path, - alg.to_owned(), - tsa_url, - )?), - "es256" | "es384" | "es512" => Box::new(EcSigner::from_files( - &signcert_path, - &pkey_path, - alg.to_owned(), - tsa_url, - )?), - "ed25519" => Box::new(EdSigner::from_files( - &signcert_path, - &pkey_path, - alg.to_owned(), - tsa_url, - )?), - _ => return Err(Error::BadParam(alg.to_owned())), - }) -} diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 7521e5323..03726c87f 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -1460,8 +1460,7 @@ impl Store { // update XMP info & add xmp hash to provenance claim #[cfg(feature = "xmp_write")] if let Some(provenance) = self.provenance_path() { - embedded_xmp::add_manifest_uri_to_file(output_path, &provenance) - .map_err(|_err| Error::XmpWriteError)?; + embedded_xmp::add_manifest_uri_to_file(output_path, &provenance)?; } else { return Err(Error::XmpWriteError); } diff --git a/sdk/src/utils/test.rs b/sdk/src/utils/test.rs index ede4fbae4..1dbddbb70 100644 --- a/sdk/src/utils/test.rs +++ b/sdk/src/utils/test.rs @@ -23,7 +23,7 @@ use crate::{ #[cfg(feature = "file_io")] use crate::{ - get_signer_from_files, + create_signer, openssl::RsaSigner, signer::{ConfigurableSigner, Signer}, }; @@ -234,7 +234,7 @@ pub fn temp_signer_with_alg(alg: &str) -> Box { pem_key_path.push(alg); pem_key_path.set_extension("pem"); - get_signer_from_files(sign_cert_path.clone(), pem_key_path, alg, None) + create_signer::from_files(sign_cert_path.clone(), pem_key_path, alg, None) .expect("get_temp_signer_with_alg") } diff --git a/sdk/src/validation_status.rs b/sdk/src/validation_status.rs index 3b3687771..c847db33f 100644 --- a/sdk/src/validation_status.rs +++ b/sdk/src/validation_status.rs @@ -45,9 +45,9 @@ pub struct ValidationStatus { } impl ValidationStatus { - pub(crate) fn new(code: String) -> Self { + pub(crate) fn new>(code: S) -> Self { Self { - code, + code: code.into(), url: None, explanation: None, } diff --git a/sdk/src/wasm/webcrypto_validator.rs b/sdk/src/wasm/webcrypto_validator.rs index 58f7a5c52..57887c892 100644 --- a/sdk/src/wasm/webcrypto_validator.rs +++ b/sdk/src/wasm/webcrypto_validator.rs @@ -11,39 +11,18 @@ // specific language governing permissions and limitations under // each license. +use crate::utils::hash_utils::hash_by_alg; use crate::wasm::context::WindowOrWorker; use crate::{Error, Result}; use js_sys::{Array, ArrayBuffer, Object, Reflect, Uint8Array}; +use rsa::{BigUint, PaddingScheme, PublicKey, RsaPublicKey}; +use sha2::{Sha256, Sha384, Sha512}; +use spki::SubjectPublicKeyInfo; +use std::convert::TryFrom; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use web_sys::{CryptoKey, SubtleCrypto}; -pub struct RsaHashedImportParams { - name: String, - hash: String, -} - -impl RsaHashedImportParams { - pub fn new(name: &str, hash: &str) -> Self { - RsaHashedImportParams { - name: name.to_owned(), - hash: hash.to_owned(), - } - } - - pub fn as_js_object(&self) -> Object { - let obj = Object::new(); - Reflect::set(&obj, &"name".into(), &self.name.clone().into()).expect("not valid name"); - - let inner_obj = Object::new(); - Reflect::set(&inner_obj, &"name".into(), &self.hash.clone().into()) - .expect("not valid name"); - - Reflect::set(&obj, &"hash".into(), &inner_obj).expect("not valid name"); - - obj - } -} - +use x509_parser::der_parser::ber::{parse_ber_sequence, BerObject}; pub struct EcKeyImportParams { name: String, named_curve: String, @@ -75,26 +54,6 @@ impl EcKeyImportParams { } } -pub struct RsaPssParams { - name: String, - salt_length: u32, -} - -impl RsaPssParams { - pub fn new(name: &str, salt_length: u32) -> Self { - RsaPssParams { - name: name.to_owned(), - salt_length, - } - } - - pub fn as_js_object(&self) -> Object { - let obj = Object::new(); - Reflect::set(&obj, &"name".into(), &self.name.clone().into()).expect("not valid name"); - Reflect::set(&obj, &"saltLength".into(), &self.salt_length.into()).expect("not valid name"); - obj - } -} pub struct EcdsaParams { name: String, hash: String, @@ -128,22 +87,6 @@ fn data_as_array_buffer(data: &[u8]) -> ArrayBuffer { typed_array.buffer() } -// Alternate salt length computation function for signed data that doesn't adhere to the conventional -// salt length in the RSA-PSS spec, which should equal the length of the hash function in bytes -fn alternate_salt_length(crypto_key: &CryptoKey, salt_len: &u32) -> Result { - let algo: Object = crypto_key - .algorithm() - .map_err(|_err| Error::WasmKey)? - .into(); - let key_size: f64 = js_sys::Reflect::get(&algo, &"modulusLength".into()) - .map_err(|_err| Error::WasmKey)? - .as_f64() - .ok_or(Error::WasmKey)? - .into(); - let key_byte_len: f32 = (key_size as f32 - 1.0) / 8.0; - Ok((key_byte_len.ceil() as u32) - salt_len - 2) -} - async fn crypto_is_verified( subtle_crypto: &SubtleCrypto, alg: &Object, @@ -163,6 +106,32 @@ async fn crypto_is_verified( Ok(result) } +// Conversion utility from num-bigint::BigUint (used by x509_parser) +// to num-bigint-dig::BigUint (used by rsa) +fn biguint_val(ber_object: &BerObject) -> BigUint { + ber_object + .as_biguint() + .map(|x| x.to_u32_digits()) + .map(BigUint::new) + .unwrap_or_default() +} + +fn pss_padding_from_hash(hash: &str, salt_len: &u32) -> Result { + let salt_len = usize::try_from(salt_len.clone()) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + let rng = rand::thread_rng(); + + match hash { + "SHA-256" => Ok(PaddingScheme::new_pss_with_salt::(rng, salt_len)), + "SHA-384" => Ok(PaddingScheme::new_pss_with_salt::(rng, salt_len)), + "SHA-512" => Ok(PaddingScheme::new_pss_with_salt::(rng, salt_len)), + &_ => Err(Error::WasmRsaKeyImport(format!( + "Invalid PSS hash supplied for padding: {}", + hash + ))), + } +} + async fn async_validate( algo: String, hash: String, @@ -178,85 +147,29 @@ async fn async_validate( match algo.as_ref() { "RSA-PSS" => { - // Create key - let mut algorithm = RsaHashedImportParams::new(&algo, &hash).as_js_object(); - let key_array_buf = data_as_array_buffer(&pkey); - let usages = Array::new(); - usages.push(&"verify".into()); - - let promise = subtle_crypto - .import_key_with_object("spki", &key_array_buf, &algorithm, true, &usages) - .map_err(|_err| Error::WasmKey)?; - let crypto_key: CryptoKey = JsFuture::from(promise) - .await - .map_err(|_err| Error::WasmKey)? - .into(); - web_sys::console::debug_2(&"CryptoKey".into(), &crypto_key); - - // Create verifier - // WebCrypto requires us to pass in the salt length to validate the signature unlike some other implementations. - // Certain beta images don't use the conventional salt length in the RSA-PSS specification, which should equal - // the length of the output of the hash function in bytes. - // First, let's try to validate with the conventional salt length: - algorithm = RsaPssParams::new(&algo, salt_len).as_js_object(); - web_sys::console::debug_2( - &"Attempting verification with salt length".into(), - &salt_len.into(), - ); - let verified = crypto_is_verified( - &subtle_crypto, - &algorithm, - &crypto_key, - &sig_array_buf, - &data_array_buf, - ) - .await?; - if verified { - Ok(verified) - } else { - // If this doesn't work, we can try validating against an alternate salt length: - let salt_len = alternate_salt_length(&crypto_key, &salt_len)?; - web_sys::console::debug_2( - &"Attempting fallback verification with salt length".into(), - &salt_len.into(), - ); - algorithm = RsaPssParams::new(&algo, salt_len).as_js_object(); - crypto_is_verified( - &subtle_crypto, - &algorithm, - &crypto_key, - &sig_array_buf, - &data_array_buf, - ) - .await + let spki = SubjectPublicKeyInfo::try_from(pkey.as_ref()) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + let (_, seq) = parse_ber_sequence(spki.subject_public_key) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + let hashed_data = hash_by_alg(&hash, &data, None); + let modulus = biguint_val(&seq[0]); + let exp = biguint_val(&seq[1]); + let public_key = RsaPublicKey::new(modulus, exp) + .map_err(|err| Error::WasmRsaKeyImport(err.to_string()))?; + let padding = pss_padding_from_hash(&hash, &salt_len)?; + let result = public_key.verify(padding, &hashed_data, &sig); + + match result { + Ok(()) => Ok(true), + Err(err) => { + web_sys::console::debug_2( + &"RSA-PSS validation failed:".into(), + &err.to_string().into(), + ); + Ok(false) + } } } - "RSASSA-PKCS1-v1_5" => { - // Create Key - let algorithm = RsaHashedImportParams::new(&algo, &hash).as_js_object(); - let key_array_buf = data_as_array_buffer(&pkey); - let usages = Array::new(); - usages.push(&"verify".into()); - - let promise = subtle_crypto - .import_key_with_object("spki", &key_array_buf, &algorithm, true, &usages) - .map_err(|_err| Error::WasmKey)?; - let crypto_key: CryptoKey = JsFuture::from(promise) - .await - .map_err(|_err| Error::WasmKey)? - .into(); - web_sys::console::debug_2(&"CryptoKey".into(), &crypto_key); - - // Create verifier - crypto_is_verified( - &subtle_crypto, - &algorithm, - &crypto_key, - &sig_array_buf, - &data_array_buf, - ) - .await - } "ECDSA" => { // Create Key let named_curve = match hash.as_ref() { @@ -331,39 +244,6 @@ pub async fn validate_async(alg: &str, sig: &[u8], data: &[u8], pkey: &[u8]) -> ) .await } - "rs256" => { - async_validate( - "RSASSA-PKCS1-v1_5".to_string(), - "SHA-256".to_string(), - 0, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ) - .await - } - "rs384" => { - async_validate( - "RSASSA-PKCS1-v1_5".to_string(), - "SHA-384".to_string(), - 0, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ) - .await - } - "rs512" => { - async_validate( - "RSASSA-PKCS1-v1_5".to_string(), - "SHA-512".to_string(), - 0, - pkey.to_vec(), - sig.to_vec(), - data.to_vec(), - ) - .await - } "es256" => { async_validate( "ECDSA".to_string(), @@ -416,26 +296,32 @@ pub mod tests { #[cfg_attr(not(target_arch = "wasm32"), test)] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] #[wasm_bindgen_test] - async fn test_async_verify_good() { + async fn test_async_verify_rsa_pss() { // PS signatures - let sig_bytes = include_bytes!("../../tests/fixtures/sig.data"); - let data_bytes = include_bytes!("../../tests/fixtures/data.data"); - let key_bytes = include_bytes!("../../tests/fixtures/key.data"); + let sig_bytes = include_bytes!("../../tests/fixtures/sig_ps256.data"); + let data_bytes = include_bytes!("../../tests/fixtures/data_ps256.data"); + let key_bytes = include_bytes!("../../tests/fixtures/key_ps256.data"); - let mut validated = validate_async("ps256", sig_bytes, data_bytes, key_bytes) + let validated = validate_async("ps256", sig_bytes, data_bytes, key_bytes) .await .unwrap(); assert_eq!(validated, true); + } + #[cfg_attr(not(target_arch = "wasm32"), test)] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[wasm_bindgen_test] + async fn test_async_verify_ecdsa() { // EC signatures let sig_es384_bytes = include_bytes!("../../tests/fixtures/sig_es384.data"); let data_es384_bytes = include_bytes!("../../tests/fixtures/data_es384.data"); let key_es384_bytes = include_bytes!("../../tests/fixtures/key_es384.data"); - validated = validate_async("es384", sig_es384_bytes, data_es384_bytes, key_es384_bytes) - .await - .unwrap(); + let mut validated = + validate_async("es384", sig_es384_bytes, data_es384_bytes, key_es384_bytes) + .await + .unwrap(); assert_eq!(validated, true); @@ -465,9 +351,9 @@ pub mod tests { #[wasm_bindgen_test] #[ignore] async fn test_async_verify_bad() { - let sig_bytes = include_bytes!("../../tests/fixtures/sig.data"); - let data_bytes = include_bytes!("../../tests/fixtures/data.data"); - let key_bytes = include_bytes!("../../tests/fixtures/key.data"); + let sig_bytes = include_bytes!("../../tests/fixtures/sig_ps256.data"); + let data_bytes = include_bytes!("../../tests/fixtures/data_ps256.data"); + let key_bytes = include_bytes!("../../tests/fixtures/key_ps256.data"); let mut bad_bytes = data_bytes.to_vec(); bad_bytes[0] = b'c'; diff --git a/sdk/tests/fixtures/data.data b/sdk/tests/fixtures/data.data deleted file mode 100644 index 5394d5e43..000000000 --- a/sdk/tests/fixtures/data.data +++ /dev/null @@ -1,4 +0,0 @@ -jSignature1D8$@Y֦idc:format`jinstanceID`oclaim_generatoroadobe unit testisignaturexRself#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.signaturejassertionscurl x`self#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.assertions/c2pa.actionscalgfsha256dhashX WKVn|jO#;~a/*O+curl xcself#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.assertions/c2pa.cloud-datacalgfsha256dhashX Ѕ@}4&snxcV -HP2#curl xgself#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.assertions/c2pa.location.broadcalgfsha256dhashX ku}kB -r^:d$+KO{pCxEcurl xiself#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.assertions/c2pa.location.precisecalgfsha256dhashX $s`} m -Q!%\Kpcurl xrself#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.assertions/c2pa.thumbnail.ingredient.jpegcalgfsha256dhashX S1RCyH%DZFȽZq>쭣curl xuself#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.assertions/c2pa.thumbnail.ingredient__1.jpegcalgfsha256dhashX S1RCyH%DZFȽZq>쭣curl xuself#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.assertions/c2pa.thumbnail.ingredient__2.jpegcalgfsha256dhashX S1RCyH%DZFȽZq>쭣curl xbself#jumbf=c2pa/adobe:urn:uuid:30843618-ff1c-4783-a106-1a51d396a6a8/c2pa.assertions/c2pa.hash.datacalgfsha256dhashX br N>)c7pa[רHf)lcalgfsha256 \ No newline at end of file diff --git a/sdk/tests/fixtures/data_ps256.data b/sdk/tests/fixtures/data_ps256.data new file mode 100644 index 000000000..9cf627903 --- /dev/null +++ b/sdk/tests/fixtures/data_ps256.data @@ -0,0 +1 @@ +jSignature1D8$@YChdc:titleeC.jpgidc:formatjimage/jpegjinstanceIDx,xmp:iid:fc3529a4-92e0-4786-af3c-13d124303649oclaim_generatorx$make_test_images/0.7.0 c2pa-rs/0.7.0isignaturexself#jumbf=c2pa.signaturejassertionscurlx4self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpegdhashX B>@rgW\DN9ip竫*p:ޢcurlx7self#jumbf=c2pa.assertions/stds.schema-org.CreativeWorkdhashX Hcmry?1~[~g¥!J¯rGcurlx'self#jumbf=c2pa.assertions/c2pa.actionsdhashX it+x!"NXIiks ʯcurlx)self#jumbf=c2pa.assertions/c2pa.hash.datadhashX I LNOPfeAvcalgfsha256 \ No newline at end of file diff --git a/sdk/tests/fixtures/key.data b/sdk/tests/fixtures/key.data deleted file mode 100644 index 056ef3d6b..000000000 Binary files a/sdk/tests/fixtures/key.data and /dev/null differ diff --git a/sdk/tests/fixtures/key_ps256.data b/sdk/tests/fixtures/key_ps256.data new file mode 100644 index 000000000..ec6b57a5e Binary files /dev/null and b/sdk/tests/fixtures/key_ps256.data differ diff --git a/sdk/tests/fixtures/sig.data b/sdk/tests/fixtures/sig.data deleted file mode 100644 index 1613a8630..000000000 Binary files a/sdk/tests/fixtures/sig.data and /dev/null differ diff --git a/sdk/tests/fixtures/sig_ps256.data b/sdk/tests/fixtures/sig_ps256.data new file mode 100644 index 000000000..bfb70c48b Binary files /dev/null and b/sdk/tests/fixtures/sig_ps256.data differ diff --git a/sdk/tests/integration.rs b/sdk/tests/integration.rs index c8f440a94..e63bdd859 100644 --- a/sdk/tests/integration.rs +++ b/sdk/tests/integration.rs @@ -18,7 +18,7 @@ mod integration_1 { use c2pa::{ assertions::{c2pa_action, Action, Actions}, - get_signer_from_files, Ingredient, Manifest, ManifestStore, Result, Signer, + create_signer, Ingredient, Manifest, ManifestStore, Result, Signer, }; use std::path::PathBuf; use tempfile::tempdir; @@ -31,7 +31,7 @@ mod integration_1 { signcert_path.push("tests/fixtures/certs/ps256.pub"); let mut pkey_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); pkey_path.push("tests/fixtures/certs/ps256.pem"); - get_signer_from_files(signcert_path, pkey_path, "ps256", None) + create_signer::from_files(signcert_path, pkey_path, "ps256", None) .expect("get_signer_from_files") }