commit 6f91da0c3add42d0d77494bc57701dbd9e6aa4e9
parent 9da7a0785e5b0e0bd0a66f592ddf69ab25c55f30
Author: Eric Scouten <scouten@adobe.com>
Date: Wed, 8 Jun 2022 14:44:20 -0700
Remove c2patool source now that it's in its own repo (#35)
c2patool now lives in its own repo (https://github.com/contentauth/c2patool); remove it from here.
Co-authored-by: Gavin Peacock <gpeacock@adobe.com>
Diffstat:
14 files changed, 3 insertions(+), 927 deletions(-)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
@@ -9,11 +9,6 @@ updates:
interval: "daily"
- package-ecosystem: "cargo"
- directory: "c2patool"
- schedule:
- interval: "daily"
-
- - package-ecosystem: "cargo"
directory: "make_test_images"
schedule:
interval: "daily"
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
@@ -98,9 +98,3 @@ jobs:
cargo publish --token $CRATES_IO_SECRET -p c2pa
env:
CRATES_IO_SECRET: ${{ secrets.CRATES_IO_SECRET }}
-
- - name: Publish c2patool crate
- run: |
- cargo publish --token $CRATES_IO_SECRET -p c2patool
- env:
- CRATES_IO_SECRET: ${{ secrets.CRATES_IO_SECRET }}
diff --git a/Cargo.toml b/Cargo.toml
@@ -1,2 +1,2 @@
[workspace]
-members = ["sdk", "c2patool", "make_test_images"]
+members = ["sdk", "make_test_images"]
diff --git a/Makefile b/Makefile
@@ -32,49 +32,6 @@ test-wasm:
# Run this before pushing a PR to pre-validate
test: check-format check-docs clippy test-local test-wasm
-# Creates a folder wtih c2patool bin, samples and readme
-c2patool-package:
- rm -rf target/c2patool*
- mkdir -p target/c2patool
- mkdir -p target/c2patool/sample
- cp target/release/c2patool target/c2patool/c2patool
- cp c2patool/src/README.md target/c2patool/README.md
- cp c2patool/sample/* target/c2patool/sample
- cp CHANGELOG.md target/c2patool/CHANGELOG.md
- cp sdk/tests/fixtures/IMG_0003.jpg target/c2patool/image.jpg
-
-# These are for building the c2patool release bin on various platforms
-build-release-win:
- cargo build --release
-
-build-release-mac-arm:
- rustup target add aarch64-apple-darwin
- MACOSX_DEPLOYMENT_TARGET=11.1 cargo build --target=aarch64-apple-darwin --release
-
-build-release-mac-x86:
- rustup target add x86_64-apple-darwin
- MACOSX_DEPLOYMENT_TARGET=10.15 cargo build --target=x86_64-apple-darwin --release
-
-build-release-mac-universal: build-release-mac-arm build-release-mac-x86
- lipo -create -output target/release/c2patool target/aarch64-apple-darwin/release/c2patool target/x86_64-apple-darwin/release/c2patool
-
-build-release-linux:
- cargo build --release
-
-# Builds and packages a zip for c2patool for each platform
-ifeq ($(PLATFORM), mac)
-c2patool-release: build-release-mac-universal c2patool-package
- cd target && zip -r c2patool_mac.zip c2patool && cd ..
-endif
-ifeq ($(PLATFORM), win)
-c2patool-release: build-release-win c2patool-package
- cd target && tar.exe -a -c -f c2patool_win.zip c2patool && cd ..
-endif
-ifeq ($(PLATFORM), linux)
-c2patool-release: build-release-linux c2patool-package
- cd target && tar -czvf c2patool_linux.tar.gz c2patool && cd ..
-endif
-
# Builds and views documentation
doc:
cargo doc --no-deps --open
diff --git a/README.md b/README.md
@@ -1,8 +1,8 @@
-# C2PA Rust SDK and Command-Line Tool
+# C2PA Rust SDK
The **[Coalition for Content Provenance and Authenticity](https://c2pa.org)** (C2PA) addresses the prevalence of misleading information online through the development of technical standards for certifying the source and history (or provenance) of media content. C2PA is a Joint Development Foundation project, formed through an alliance between Adobe, Arm, Intel, Microsoft and Truepic.
-This Rust library and command-line tool for creating and inspecting C2PA data structures are created by Adobe and other contributors as part of our work on the [Content Authenticity Initiative](https://contentauthenticity.org).
+This Rust library for creating and inspecting C2PA data structures are created by Adobe and other contributors as part of our work on the [Content Authenticity Initiative](https://contentauthenticity.org).
## Key Features
diff --git a/c2patool/Cargo.toml b/c2patool/Cargo.toml
@@ -1,22 +0,0 @@
-[package]
-name = "c2patool"
-version = "0.2.0"
-authors = ["Gavin Peacock <gpeacock@adobe.com>", "Maurice Fisher <mfisher@adobe.com>"]
-license = "MIT OR Apache-2.0"
-edition = "2018"
-rust-version = "1.58.0"
-
-[dependencies]
-anyhow = "1.0"
-c2pa = { path = "../sdk", features = ["file_io"] }
-env_logger = "0.9"
-log = "0.4"
-serde = { version = "1.0", features = ["derive"] }
-serde_derive = "1.0"
-serde_json = "1.0"
-structopt = "0.3"
-tempfile = "3.3"
-
-[dev-dependencies]
-assert_cmd = "2.0"
-predicates = "2.1"
diff --git a/c2patool/sample/config.json b/c2patool/sample/config.json
@@ -1,57 +0,0 @@
-{
- "alg": "es256",
- "private_key": "es256_private.key",
- "sign_cert": "es256_certs.pem",
- "ta": "http://timestamp.digicert.com",
-
- "vendor": "myvendor",
- "claim_generator": "TestApp",
- "title": "My Title",
- "assertions": [
- {
- "label": "stds.schema-org.CreativeWork",
- "data": {
- "@context": "https://schema.org",
- "@type": "CreativeWork",
- "author": [
- {
- "@type": "Person",
- "name": "Joe Bloggs"
- }
- ],
- "url": "https://contentauthenticity.org/"
- }
- },
- {
- "label": "c2pa.actions",
- "data": {
- "actions": [
- {
- "action": "c2pa.opened"
- },
- {
- "action": "c2pa.edited",
- "parameters": {
- "name": "brightnesscontrast"
- }
- }
- ],
- "metadata": {
- "reviewRatings": [
- {
- "code": "c2pa.unknown",
- "explanation": "Something untracked happened",
- "value": 4
- }
- ]
- }
- }
- },
- {
- "label": "my.assertion",
- "data": {
- "any_tag": "whatever I want"
- }
- }
- ]
-}
-\ No newline at end of file
diff --git a/c2patool/sample/es256_certs.pem b/c2patool/sample/es256_certs.pem
@@ -1,39 +0,0 @@
-Bag Attributes
- localKeyID: 21 9D 38 2E 7C 25 38 78 94 3F CA DA 5A A7 BC BA 3F 7F 24 21
-subject=/O=Media Publisher Company/CN=Bob
-issuer=/O=Media Publisher Company/CN=Media Publisher Company Intermediate CA
------BEGIN CERTIFICATE-----
-MIICQDCCAaGgAwIBAgIUXsGqKw4Bw9PJBv1BcL3SLGiyCT4wCgYIKoZIzj0EAwIw
-VDEgMB4GA1UECgwXTWVkaWEgUHVibGlzaGVyIENvbXBhbnkxMDAuBgNVBAMMJ01l
-ZGlhIFB1Ymxpc2hlciBDb21wYW55IEludGVybWVkaWF0ZSBDQTAeFw0yMjA0MDQx
-NDE1NDhaFw0yMzA0MDQxNDE1NDhaMDAxIDAeBgNVBAoMF01lZGlhIFB1Ymxpc2hl
-ciBDb21wYW55MQwwCgYDVQQDDANCb2IwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
-AAQv+8qHEYohEJwVLKQIqJX6se0E+CKk89bwRYRRfCsmHeZuFLdBcJ082I67mpSM
-zzFN1eSX82Mdphb7+t/uIKPko3UwczAOBgNVHQ8BAf8EBAMCBsAwFgYDVR0lAQH/
-BAwwCgYIKwYBBQUHAwQwCQYDVR0TBAIwADAdBgNVHQ4EFgQUTqgwjgSAknNN8T+e
-bNXm+mbtlfIwHwYDVR0jBBgwFoAUXF8IatqTvlwmGnVmM2L6+v1IQMcwCgYIKoZI
-zj0EAwIDgYwAMIGIAkIAkZ0LAaJ209QLyiSn/hIMfbBReg+d61gX8U+9OqBWYiD2
-i6u59mJrKdwCuj8po8jh7ntkcXHc1v+3ztHWCHCI9R0CQgErPKUhrxei5mbKU0Xx
-NUsTBB6oHMZccZCn1FS0R7YaCFume2mscC1rGGNXqu/Skgsq6FPkFHJqyFTZhtcW
-pJWKaw==
------END CERTIFICATE-----
-Bag Attributes: <No Attributes>
-subject=/O=Media Publisher Company/CN=Media Publisher Company Intermediate CA
-issuer=/CN=Media Provenance Intermediate CA 1
------BEGIN CERTIFICATE-----
-MIICbTCCAc+gAwIBAgIUA7qQpsd9jsBL7dahNfBx+ftJ5VQwCgYIKoZIzj0EAwQw
-LTErMCkGA1UEAwwiTWVkaWEgUHJvdmVuYW5jZSBJbnRlcm1lZGlhdGUgQ0EgMTAe
-Fw0yMjA0MDQxNDE1MDRaFw0zMjAzMzExNDE1MDRaMFQxIDAeBgNVBAoMF01lZGlh
-IFB1Ymxpc2hlciBDb21wYW55MTAwLgYDVQQDDCdNZWRpYSBQdWJsaXNoZXIgQ29t
-cGFueSBJbnRlcm1lZGlhdGUgQ0EwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABABo
-g4jSfIvYPwpADEOiQjWOSD5KXTJl9k/gz0vVpE1D3gdn5TK2UiuEiKiiZvND45pi
-U/TW0jVs6Rfns7mBTKEAygFGbcjEVqMZTfXcWYIi2AvLARe/HCeVMO3x5g4AmDmr
-CgshTHWNwrit6u/ae9YdOv5QdqBLKW6NRdvv4jvpZpK/QKNjMGEwHQYDVR0OBBYE
-FFxfCGrak75cJhp1ZjNi+vr9SEDHMB8GA1UdIwQYMBaAFP59tM4KPiaHG81hkWz1
-1CoFfPYiMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49
-BAMEA4GLADCBhwJCAeYlt/gDEOc0Y5NFLTcf3tOrwYR+m4Gn+bbgrdKznU2ygm0P
-PqKHjCT+bMZmO7NJHHcwRg245AeKp1tCJ/nM/3+FAkFMQq5CaD6l7meYE+8wFtlD
-vXlpFWg3ryuroc/DDBFMyrTZKWCz3wl5bLfx4GoWvdsrFBdClrhTO2srJmmQKH6t
-QA==
------END CERTIFICATE-----
-
diff --git a/c2patool/sample/es256_private.key b/c2patool/sample/es256_private.key
@@ -1,5 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MHcCAQEEIL2yf62xPPFBKbDcBEKU5HIickmG2DEDcZt0lD3N4aOLoAoGCCqGSM49
-AwEHoUQDQgAEL/vKhxGKIRCcFSykCKiV+rHtBPgipPPW8EWEUXwrJh3mbhS3QXCd
-PNiOu5qUjM8xTdXkl/NjHaYW+/rf7iCj5A==
------END EC PRIVATE KEY-----
diff --git a/c2patool/src/README.md b/c2patool/src/README.md
@@ -1,189 +0,0 @@
-# c2paTool
-
-Command line tool for displaying and adding C2PA manifests
-A file path to a JPEG or a claim definition JSON file must be provided
-If a JPEG path is given, this will generate a summary report of any manifests in that file
-If a manifest definition JSON file is specified, the manifest will be created and displayed in a JSON report
-
-## Displaying Manifest data
-
-Invoking the tool with a path to an image file will output a JSON report of the Manifests in the file
-File formats supported are jpeg and png.
-
-```c2patool image.jpg```
-
-## Displaying detailed Manifest data
-
-The -d option will output a detailed JSON report of the internal C2PA structure
-
-```c2patool image.jpg -d```
-
-## Previewing a Manifest
-
-If a path to a json config file is given,
-the tool will generate a new manifest using the values given in definition
-this will display the results but not save anything unless an output (-o) is specified
-
-```c2patool sample/config.json```
-
-The config json can also be passed on the command line as string using the -c --config option
-
-```shell
-c2patool -c '{"assertions": [{"label": "org.contentauth.test", "data": {"name": "Jane Doe"}}]}'
-```
-
-## Creating a new output image
-
-A file path for creating an output file with any added claim data
-If the output file already exists, any C2PA data in that file will be replaced and the image maintained
-If the output file doesn't exist, a parent file must be available for a source image
-If you are not changing an image and just adding C2PA data, use an existing output file and no parent
-If you have edited an image and want to add C2PA data to it, pass the original as the parent
-and put the edited file at the output location to have the C2PA data added.
-
-```c2patool sample/config.json -o output.jpg```
-## Overriding the parent file
-
-When using a json file, the parent file can be specified by passing -p or --parent with the path to the file
-This allows adding the same manifest data to different source images
-
-## Working with .c2pa manifest files
-
-If the extension of the output file is '.c2pa' a standalone manifest store will be written
-
-```c2patool claim_image.jpg -o manifest.c2pa```
-
-These .c2pa manifest files can be read by claim tool and will generate reports.
-
-```c2patool manifest.c2pa```
-## Setup
-
-Before you can add a manifest, you need to create an SSL certificate
-You can specify the path to the cert files in the configuration fields
-```
-private_key
-sign_cert
-```
-If you are using a signing algorithm other than the default ps256, you will need to specify it in
-```alg```
-Which can be set to one of [ ps256 | ps384 | ps512 | es256 | es384 | es512 | ed25519] and
-must be compatible with values of private key and sign cert.
-
-The key and cert can also be placed in the environment variables C2PA_PRIVATE_KEY and C2PA_PUB_CERT
-These two variable are used to set the private key and public certificates. For example to sign with es256 signatures
-using the content of a private key file and certificate file:
-
-```set C2PA_PRIVATE_KEY=$(cat my_es256_private_key)```
-```set C2PA_PUB_CERT=$(cat my_es256_certs)```
-
-The both private key and sign cert should be in PEM format. The sign cert should contain a certificate
-chain PEMs starting for the end-entity certificate used to sign the claim ending with intermediate certificate
-before the root CA certificate. See ```sample`` folder for example certificates.
-
-To create your own temporary files for testing you can execute the following command
-
-```shell
-sudo openssl req -new -newkey rsa:4096 -sigopt rsa_padding_mode:pss -days 180 -extensions v3_ca -addext "keyUsage = digitalSignature" -addext "extendedKeyUsage = emailProtection" -nodes -x509 -keyout private.key -out certs.pem -sha256
-```
-
-Note you may have need to update your openssl version if the above command does not work.
-
-c2patool can also timestamp the signature data that is embedded. This is useful for validating an asset when the embedded
-certificates have expired. If the config has a ta_url set, c2patool will attempt to timestamp the signature using the TA service at the provided URL. The TA must be RFC3161 compliant. Example TA setting:
-
-```ta_url=http://timestamp.digicert.com```
-
-## Configuration file format
-
-The Configuration file is a JSON formatted file with a .json extension:
-
-The schema for this type is as follows:
-```json
-{
- "$schema": "http://json-schema.org/draft-07/schema",
- "$id": "http://ns.adobe.com/cai/claim-definition/v1",
- "type": "object",
- "description": "Definition format for claim created with c2patool",
- "examples": [
- {
- "vendor": "myvendor",
- "claim_generator": "MyApp/0.1",
- "parent": "image.jpg",
- "ingredients": [],
- "assertions": [
- {
- "label": "my.assertion",
- "data": {
- "any_tag": "whatever I want"
- }
- }
- ],
- "alg": "es256",
- "private_key": "es256_private.key",
- "sign_cert": "es256_certs.pem",
- "ta_url": "http://timestamp.digicert.com"
- }
- ],
- "required": [
- "assertions",
- ],
- "properties": {
- "vendor": {
- "type": "string",
- "description": "Typically an Internet domain name (without the TLD) for the vendor (i.e. `adobe`, `nytimes`)"
- },
- "claim_generator": {
- "type": "string",
- "description": "A UserAgent string that will let a user know what software/hardware/system produced this Manifest - names should not contain spaces (defaults to c2patool)"
- },
- "title": {
- "type": "string",
- "description": "A human-readable string to be displayed as the tile for this Manifest (defaults to embedded file name)"
- },
- "credentials": {
- "type": "object",
- "description": "An array of W3C verifiable credentials objects defined in the c2pa assertion specification. Section 7"
- },
- "parent": {
- "type": "string",
- "format": "Local file system path",
- "description": "A file path to the source image that was modified by this Manifest (if any)"
- },
- "Ingredients": {
- "type": "array of string",
- "format": "Array of local file system paths",
- "description": "File paths to images that were used to modify the image referenced by this Manifest (if any)"
- },
- "assertions": {
- "type": "object",
- "description": "Objects with label, and data - standard c2pa labels must match values as defined in the c2pa assertion specification"
- },
- "alg": {
- "type": "string",
- "format": "Local file system path",
- "description": "Signing algorithm: one of [ ps256 | ps384 | ps512 | es256 | es384 | es512 | ed25519]"
- },
- "ta_url": {
- "type": "string",
- "format": "http URL",
- "description": "A URL to an RFC3161 compliant Time Stamp Authority"
- },
- "private_key": {
- "type": "string",
- "format": "Local file system path",
- "description": "File path to a private key file"
- },
- "sign_cert": {
- "type": "string",
- "format": "Local file system path",
- "description": "File path to signing cert file"
- },
- "base_path": {
- "type": "string",
- "format": "Local file system path",
- "description": "File path to a folder to use as the base for relative paths in config"
- },
- },
- "additionalProperties": false
-}
-```
diff --git a/c2patool/src/config.rs b/c2patool/src/config.rs
@@ -1,53 +0,0 @@
-// 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.
-use c2pa::ManifestAssertion;
-
-use serde::Deserialize;
-use serde_json::Value;
-use std::path::PathBuf;
-
-/// Defines the components used to build a claim to embed in a manifest
-#[derive(Debug, Deserialize)]
-pub struct Config {
- /// Optional prefix added to the generated Manifest Label
- ///
- /// This is typically Internet domain name for the vendor (i.e. `adobe`)
- pub vendor: Option<String>,
- /// A User Agent formatted string identifying the software/hardware/system produced this claim
- ///
- /// Spaces are not allowed in names, versions can be specified with product/1.0 syntax
- pub claim_generator: Option<String>,
- /// Optional title to use for this claim, defaults to the output file name
- pub title: Option<String>,
- /// Optional parent ingredient file path
- pub parent: Option<PathBuf>,
- /// A List of verified credentials
- pub credentials: Option<Vec<Value>>,
- /// A list of non-parent ingredients to include
- pub ingredients: Option<Vec<PathBuf>>,
- /// A list of [ManifestAssertion] to add to this created manifest
- pub assertions: Vec<ManifestAssertion>,
- /// An optional base path to use for any relative paths in defined in this structure
- pub base_path: Option<PathBuf>,
- /// Signing algorithm to use - must match the associated certs
- ///
- /// Must be one of [ ps256 | ps384 | ps512 | es256 | es384 | es512 | ed25519 ]
- /// Defaults to ps256
- pub alg: Option<String>,
- /// A path to a file containing the private key required for signing
- pub private_key: Option<PathBuf>,
- /// A path to a file containing the signing cert required for signing
- pub sign_cert: Option<PathBuf>,
- /// A Url to a Time Authority to use when signing the manifest
- pub ta_url: Option<String>,
-}
diff --git a/c2patool/src/main.rs b/c2patool/src/main.rs
@@ -1,300 +0,0 @@
-// 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.
-
-#![doc = include_str!("README.md")]
-/// Tool to display and create C2PA manifests
-///
-/// A file path to a jpeg must be provided
-/// If only the path is given, this will generate a summary report of any claims in that file
-/// If a claim def json file is specified, the claim will be added to any existing claims
-/// If the claim def includes an asset_path, the claims in that file will be used instead
-///
-///
-use anyhow::Result;
-use c2pa::{Error, Ingredient, Manifest, ManifestStore, ManifestStoreReport};
-
-use std::{
- fs,
- path::{Path, PathBuf},
- process::exit,
-};
-use structopt::StructOpt;
-use tempfile::tempdir;
-
-pub mod config;
-use config::Config;
-mod signer;
-use signer::get_c2pa_signer;
-
-// define the command line options
-#[derive(Debug, StructOpt)]
-#[structopt(author = "Adobe", about = "Tool for displaying and creating C2PA manifests",setting = structopt::clap::AppSettings::ColoredHelp)]
-struct CliArgs {
- #[structopt(parse(from_os_str))]
- #[structopt(short = "o", long = "output", help = "path to output file")]
- output: Option<std::path::PathBuf>,
-
- #[structopt(parse(from_os_str))]
- #[structopt(short = "p", long = "parent", help = "path to parent file")]
- parent: Option<std::path::PathBuf>,
-
- #[structopt(
- short = "c",
- long = "config",
- help = "Configuration passed as json string"
- )]
- config: Option<String>,
-
- #[structopt(
- short = "d",
- long = "detailed",
- help = "display detailed internal manifest data"
- )]
- detailed: bool,
-
- /// The path to the file to read (jpg or json for adding claims)
- #[structopt(parse(from_os_str))]
- path: Option<std::path::PathBuf>,
-}
-
-// converts any relative paths to absolute from base_path
-pub fn fix_relative_path(path: &Path, base_path: &Path) -> PathBuf {
- if path.is_absolute() {
- return PathBuf::from(path);
- }
- let mut p = PathBuf::from(base_path);
- p.push(path);
- p
-}
-
-fn handle_config(
- json: &str,
- base_dir: &Path,
- parent: Option<&Path>,
- output_opt: Option<&Path>,
- is_detailed: bool,
-) -> Result<()> {
- let config: Config = serde_json::from_str(json)?;
-
- // if the config has a base path, use it for relative paths in the config
- // otherwise set the base path to the location of the config file
- let base_path = match &config.base_path {
- Some(path) => PathBuf::from(path),
- None => PathBuf::from(base_dir),
- };
-
- let signer = get_c2pa_signer(&config, &base_path)?;
-
- let claim_generator = match config.claim_generator {
- Some(claim_generator) => claim_generator,
- None => format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")),
- };
-
- let mut manifest = Manifest::new(claim_generator);
-
- if let Some(vendor) = config.vendor {
- manifest.set_vendor(vendor);
- }
-
- if let Some(credentials) = config.credentials.as_ref() {
- for credential in credentials {
- manifest.add_verifiable_credential(credential)?;
- }
- }
-
- // if claim_def has a parent, set the parent asset
- let parent = match parent {
- Some(parent) => Some(PathBuf::from(parent)),
- None => config
- .parent
- .as_deref()
- .map(|parent| fix_relative_path(parent, &base_path)),
- };
- if let Some(parent) = parent.as_ref() {
- if !parent.exists() {
- eprintln!("Parent file not found {:#?}", parent);
- exit(1);
- }
- manifest.set_parent(Ingredient::from_file(parent)?)?;
- }
-
- // add all the ingredients (claim def ingredients do not include the parent)
- if let Some(ingredients) = config.ingredients.as_ref() {
- for ingredient in ingredients {
- let path = fix_relative_path(ingredient, &base_path);
- if !path.exists() {
- eprintln!("Ingredient file not found {:#?}", path);
- exit(1);
- }
- let ingredient = Ingredient::from_file(&path).unwrap_or_else(|e| {
- eprintln!("error loading ingredient {:?} {:?}", &path, e);
- exit(1);
- });
- manifest.add_ingredient(ingredient);
- }
- }
-
- // add any assertions
- for assertion in config.assertions {
- manifest.add_labeled_assertion(assertion.label(), &assertion.value()?)?;
- }
-
- // if we have an output option, then we must have a source image to add a claim to
- // we need to determine the source file and copy it a temporary location where we will update it
- // once successfully written we can copy the temp back to the output location, possibly overwriting
- // The source can be an existing file at the output_path, or the parent file if we have one.
- if let Some(output) = output_opt {
- let file_name = match output.file_name().and_then(|s| s.to_str()) {
- Some(name) => name,
- None => {
- eprintln!("Missing or invalid filename on output");
- exit(1);
- }
- };
- // check for valid extension and do special extension handling
- let _extension = match output.extension().and_then(|s| s.to_str()) {
- Some(ext) => ext,
- None => {
- eprintln!("Missing or invalid extension on output");
- exit(1);
- }
- };
-
- // Predefine the manifest asset if we need to set a title
- // Todo: find a better way to set the title
- if let Some(t) = config.title.as_ref() {
- let mut asset = Ingredient::from_file_info(output);
- asset.set_title(t.to_owned());
- manifest.set_asset(asset);
- };
-
- // The source path points to the image we want to sign.
- // If a file already exists at the output location, we will treat that as the source
- // Otherwise, since this tool does no image editing, we can treat the parent file as the source.
- let source_path = match output.exists() {
- true => output,
- false => {
- parent.as_deref().filter(|p| p.exists()).or_else(||{
- eprintln!("A valid parent path or existing output file is required for claim embedding");
- exit(1);
- }).unwrap()
- }
- };
-
- // Embed to a temporary file and then rename or copy back to the output.
- // This way we never have a half written manifest if something fails.
- let dir = tempdir()?;
-
- // temp file_name must match output file name, it may be used as the claim title
- let temp_path = dir.path().join(&file_name);
-
- manifest
- .embed(source_path, &temp_path, signer.as_ref())
- .unwrap_or_else(|e| {
- eprintln!("error embedding manifest: {:?}", e);
- exit(1);
- });
-
- // embed completed successfully, now rename to the target path
- std::fs::rename(&temp_path, &output)
- // if rename fails, try to copy in case we are on different volumes
- .or_else(|_| std::fs::copy(&temp_path, &output).and(Ok(())))
- .map_err(Error::IoError)?;
-
- // print a report on the output file
- report_from_path(&output, is_detailed);
-
- Ok(())
- } else {
- if is_detailed {
- eprintln!("detailed report not supported for preview")
- } else {
- println!("{}", ManifestStore::from_manifest(&manifest)?);
- }
- Ok(())
- }
-}
-
-// prints the requested kind of report or exits with error
-fn report_from_path<P: AsRef<Path>>(path: &P, is_detailed: bool) {
- let report = match is_detailed {
- true => ManifestStoreReport::from_file(path).map(|r| r.to_string()),
- false => ManifestStore::from_file(path).map(|r| r.to_string()),
- };
- match report {
- Ok(report) => {
- println!("{}", report);
- }
- Err(Error::JumbfNotFound) | Err(Error::LogStop) => {
- println!("No claim found");
- exit(1)
- }
- Err(Error::PrereleaseError) => {
- eprintln!("Prerelease claim found");
- exit(1)
- }
- Err(e) => {
- println!("Error Loading {:?} {:?}", &path.as_ref(), e);
- exit(1);
- }
- }
-}
-
-fn main() -> Result<()> {
- let args = CliArgs::from_args();
-
- // set RUST_LOG=debug to get detailed debug logging
- if std::env::var("RUST_LOG").is_err() {
- std::env::set_var("RUST_LOG", "error");
- }
- env_logger::init();
-
- let mut config = args.config;
- let mut base_dir = PathBuf::from(".");
-
- if let Some(path) = args.path.clone() {
- if !path.exists() {
- println!("File not found {:?}", path);
- exit(1);
- }
-
- let extension = path.extension().and_then(|p| p.to_str()).unwrap_or("");
- // path can be a jpeg source file or a json working claim description
- match extension {
- "jpg" | "jpeg" | "png" | "c2pa" => {
- report_from_path(&path, args.detailed);
- }
- "json" => {
- // file paths in Config are relative to the json file
- base_dir = PathBuf::from(&path);
- base_dir.pop();
-
- config = Some(fs::read_to_string(&path)?);
- }
- _ => {
- println!("Unsupported file type {}", extension);
- exit(1);
- }
- };
- }
-
- if let Some(json) = config {
- handle_config(
- &json,
- &base_dir,
- args.parent.as_deref(),
- args.output.as_deref(),
- args.detailed,
- )?;
- }
- Ok(())
-}
diff --git a/c2patool/src/signer.rs b/c2patool/src/signer.rs
@@ -1,81 +0,0 @@
-// 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.
-
-/// Provides a method to read configured certs and generate a singer
-///
-use crate::{config::Config, fix_relative_path};
-
-use anyhow::{Context, Result};
-use c2pa::{get_signer, Signer};
-
-use std::{env, path::Path, process::exit};
-
-pub fn get_ta_url() -> Option<String> {
- std::env::var("C2PA_TA_URL").ok()
-}
-
-/// Generates a signature from local keys specified by the environment
-/// keys can be directly in environment variables
-/// or in a folder referenced by CAI_KEY_PATH
-/// also supports default dev environment keys
-pub fn get_c2pa_signer(config: &Config, base_path: &Path) -> Result<Box<dyn Signer>> {
- let alg = config.alg.as_deref().unwrap_or("ps256").to_lowercase();
- let tsa_url = config.ta_url.clone().or_else(get_ta_url);
-
- let mut private_key = None;
- let mut sign_cert = None;
-
- if let Some(path) = config.private_key.as_deref() {
- let path = fix_relative_path(path, base_path);
- private_key =
- Some(std::fs::read(&path).context(format!("Reading private key: {:?}", &path))?);
- }
- if private_key.is_none() {
- if let Ok(key) = env::var("C2PA_PRIVATE_KEY") {
- private_key = Some(key.as_bytes().to_vec());
- }
- };
-
- if let Some(path) = config.sign_cert.as_deref() {
- let path = fix_relative_path(path, base_path);
- sign_cert = Some(std::fs::read(&path).context(format!("Reading sign cert: {:?}", &path))?);
- }
- if sign_cert.is_none() {
- if let Ok(cert) = env::var("C2PA_SIGN_CERT") {
- sign_cert = Some(cert.as_bytes().to_vec());
- }
- };
-
- if let Some(private_key) = private_key {
- if let Some(sign_cert) = sign_cert {
- let signer = get_signer(&sign_cert, &private_key, &alg, tsa_url)?;
- return Ok(signer);
- }
- }
-
- eprintln!(
- "\n\n-----------\n\n\
- Claim creation requires a private key and signing certificate \n\
- Set the config file fields, private_key and sign_cert to paths to the required files.
- \n\
- You can generate a throwaway RSAPSS SSH private key and cert for testing on macos by \n\
- pasting the following line into a terminal and hitting enter\n\
- openssl req -new -newkey rsa:4096 -sigopt rsa_padding_mode:pss -days 180 -extensions v3_ca -addext \"keyUsage = digitalSignature\" -addext \"extendedKeyUsage = emailProtection\" -nodes -x509 -keyout private.key -out certs.pem -sha256\n\
- \n\
- You then need to reference those files in config.private_key and config.sign_cert\n\
- \n\
- The private key can alternatively be passed in the environment var C2PA_PRIVATE_KEY
- The signing cert can alternatively be passed in the environment var C2PA_SIGN_CERT
- -----------\n\n");
- exit(1);
-}
diff --git a/c2patool/tests/integration.rs b/c2patool/tests/integration.rs
@@ -1,123 +0,0 @@
-// 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.
-
-// isolate from wasm by wrapping in module
-#[cfg(not(target_arch = "wasm32"))]
-mod integration {
- use assert_cmd::prelude::*; // Add methods on commands
- use predicates::prelude::*;
- use std::path::PathBuf;
- use std::process::Command;
-
- const TEST_IMAGE: &str = "earth_apollo17.jpg";
- //const TEST_IMAGE: &str = "libpng-test.png"; // save for png testing
- //const TEST_IMAGE_WITH_MANIFEST: &str = "C.jpg"; // save for manifest tests
-
- fn fixture_path(name: &str) -> PathBuf {
- let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
- path.push("../sdk/tests/fixtures");
- path.push(name);
- std::fs::canonicalize(path).expect("canonicalize")
- }
-
- fn temp_path(name: &str) -> PathBuf {
- let mut path = PathBuf::from(env!("CARGO_TARGET_TMPDIR"));
- std::fs::create_dir_all(&path).ok();
- path.push(name);
- path
- }
-
- #[test]
- fn tool_not_found() -> Result<(), Box<dyn std::error::Error>> {
- let mut cmd = Command::cargo_bin("c2patool")?;
- cmd.arg("test/file/not.found");
- cmd.assert()
- .failure()
- .stdout(predicate::str::contains("File not found"));
- Ok(())
- }
-
- #[test]
- fn tool_version_check() {
- // ensure c2patool version matches the toolkit version
- assert_eq!(c2pa::VERSION, env!("CARGO_PKG_VERSION"));
- }
-
- #[test]
- fn tool_jpeg_no_report() -> Result<(), Box<dyn std::error::Error>> {
- let mut cmd = Command::cargo_bin("c2patool")?;
- cmd.arg(fixture_path(TEST_IMAGE));
- cmd.assert()
- .failure()
- .stdout(predicate::str::contains("No claim found"));
- Ok(())
- }
-
- #[test]
- fn tool_embed_jpeg_report() -> Result<(), Box<dyn std::error::Error>> {
- generate_x509_temp_keys();
-
- Command::cargo_bin("c2patool")?
- .arg("sample/config.json")
- .arg("-p")
- .arg(fixture_path(TEST_IMAGE))
- .arg("-o")
- .arg(temp_path("out.jpg"))
- .assert()
- .success() // should this be a failure?
- .stdout(predicate::str::contains("My Title"));
- Ok(())
- }
-
- /* remove this until the c2patool supports .c2pa write again
- #[test]
- fn tool_manifest_report() -> Result<(), Box<dyn std::error::Error>> {
- generate_x509_temp_keys();
-
- // first export a c2pa file
- Command::cargo_bin("c2patool")?
- .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST))
- .arg("-o")
- .arg(temp_path("manifest.c2pa"))
- .assert()
- .success()
- .stdout(predicate::str::contains("C2PA Testing"));
- // then read it back in
- Command::cargo_bin("c2patool")?
- .arg(temp_path("manifest.c2pa"))
- .assert()
- .success()
- .stdout(predicate::str::contains("C2PA Testing"));
- Ok(())
- }
- */
-
- fn generate_x509_temp_keys() {
- let mut x509_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
- x509_path.pop();
- x509_path.push(".x509");
-
- std::fs::create_dir_all(&x509_path).expect("Can't create .x509 dir in repo");
-
- // Test for existence of x509_path.temp_key.pub and .pem.
-
- let priv_key_path = x509_path.join("temp_key.pem");
- let sign_cert_path = x509_path.join("temp_key.pub");
-
- if !(priv_key_path.exists() && sign_cert_path.exists()) {
- // Creating the signer (which we don't use) has the side effect of
- // creating temporary private key and signing certificate.
- c2pa::get_temp_signer(&x509_path);
- }
- }
-}