diff --git a/.commitlintrc.yml b/.commitlintrc.yml index a0b1e4587..09c1eb3e1 100644 --- a/.commitlintrc.yml +++ b/.commitlintrc.yml @@ -24,6 +24,7 @@ rules: - export_schema - make_test_images - sdk + - c2patool # Scope may be empty # (NOTE: Disabled for now while we work around diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b50356831..f087d0533 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,21 +4,7 @@ version: 2 updates: - package-ecosystem: "cargo" - directory: "sdk" - schedule: - interval: "daily" - commit-message: - prefix: "update" - - - package-ecosystem: "cargo" - directory: "export_schema" - schedule: - interval: "daily" - commit-message: - prefix: "update" - - - package-ecosystem: "cargo" - directory: "make_test_images" + directory: "/" schedule: interval: "daily" commit-message: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ef578ca0..e85745af5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: fail-fast: false matrix: os: [windows-latest, macos-latest, ubuntu-latest] - rust_version: [stable, 1.76.0] + rust_version: [stable, 1.81.0] steps: - name: Checkout repository @@ -52,7 +52,7 @@ jobs: - name: Generate code coverage env: RUST_BACKTRACE: "1" - run: cargo llvm-cov --workspace --all-features --lcov --output-path lcov.info + run: cargo llvm-cov --lib --all-features --lcov --output-path lcov.info # Tokens aren't available for PRs originating from forks, # so we don't attempt to upload code coverage in that case. @@ -62,7 +62,59 @@ jobs: github.event.pull_request.author_association == 'COLLABORATOR' || github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.user.login == 'dependabot[bot]' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + verbose: true + + tests-cli: + name: Unit tests (c2patool) + if: | + github.event_name != 'pull_request' || + github.event.pull_request.author_association == 'COLLABORATOR' || + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.user.login == 'dependabot[bot]' || + contains(github.event.pull_request.labels.*.name, 'safe to test') + + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest, ubuntu-latest] + rust_version: [stable] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust_version }} + components: llvm-tools-preview + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + + - name: Generate code coverage + env: + RUST_BACKTRACE: "1" + run: cargo llvm-cov --bins --all-features --lcov --output-path lcov.info + + # Tokens aren't available for PRs originating from forks, + # so we don't attempt to upload code coverage in that case. + - name: Upload code coverage results + if: | + github.event_name != 'pull_request' || + github.event.pull_request.author_association == 'COLLABORATOR' || + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.user.login == 'dependabot[bot]' + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true @@ -117,12 +169,36 @@ jobs: github.event.pull_request.author_association == 'COLLABORATOR' || github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.user.login == 'dependabot[bot]' - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true verbose: true + cargo-check: + name: Default features build + if: | + github.event_name != 'pull_request' || + github.event.pull_request.author_association == 'COLLABORATOR' || + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.user.login == 'dependabot[bot]' || + contains(github.event.pull_request.labels.*.name, 'safe to test') + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + + - name: "`cargo check` with default features" + run: cargo check + tests-cross: name: Unit tests if: | @@ -138,7 +214,7 @@ jobs: fail-fast: false matrix: target: [aarch64-unknown-linux-gnu] - rust_version: [stable, 1.76.0] + rust_version: [stable, 1.81.0] steps: - name: Checkout repository @@ -292,7 +368,7 @@ jobs: - 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.76). + # not in stable Rust as of this writing (Rust 1.81). uses: dtolnay/rust-toolchain@nightly - name: Run cargo docs diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 5bcf246d7..d0df02853 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -147,9 +147,11 @@ jobs: echo Will add nightly suffix $NIGHTLY_SUFFIX sed -i "s/^version = \"\\(.*\\)\"/version = \"\\1$NIGHTLY_SUFFIX\"/" sdk/Cargo.toml - + sed -i "s/path = \"..\/sdk\", version = \"\\(.*\\)\"/path = \"..\/sdk\", version = \"\\1$NIGHTLY_SUFFIX\"/" cli/Cargo.toml + cargo update -w - git add -f Cargo.lock + git add Cargo.lock + find . -name 'Cargo.toml' | xargs git add echo echo Proposed changes: @@ -167,3 +169,50 @@ jobs: commit_user_name: Adobe CAI Team commit_user_email: noreply@adobe.com create_branch: true + + # ---- TO DO: Figure out how to run this job but only when nightly-snapshot changes the branch. ---- + # publish-nightly-binaries: + # name: Publish c2patool nightly binaries + # runs-on: ${{ matrix.os }} + # needs: nightly-snapshot + + # strategy: + # fail-fast: false + # matrix: + # os: [windows-latest, macos-latest, ubuntu-latest] + # include: + # - os: macos-latest + # artifact_name: c2patool_mac_universal.zip + # - os: ubuntu-latest + # artifact_name: c2patool_linux_intel.tar.gz + # - os: windows-latest + # artifact_name: c2patool_win_intel.zip + + # steps: + # - name: Checkout repository + # uses: actions/checkout@v4 + # with: + # ref: nightly + + # - name: Install Rust toolchain + # uses: dtolnay/rust-toolchain@stable + + # - name: Cache Rust dependencies + # uses: Swatinem/rust-cache@v2 + + # - name: Run cargo check + # run: cd cli && cargo check + + # - name: Run cargo test --all + # run: cd cli && cargo test --all + + # - name: Build nightly release artifacts + # run: cd cli && make release + + # - name: Upload build as artifact + # uses: actions/upload-artifact@v3 + # with: + # path: target/${{ matrix.artifact_name }} + # name: ${{ matrix.artifact_name }} + # retention-days: 15 + # if-no-files-found: error diff --git a/.github/workflows/pr_title.yml b/.github/workflows/pr_title.yml index 1942ae6f6..f18696113 100644 --- a/.github/workflows/pr_title.yml +++ b/.github/workflows/pr_title.yml @@ -42,6 +42,7 @@ jobs: # these exact names: # # * sdk (The primary C2PA Rust SDK) + # * c2patool # * export_schema # * make_test_images # @@ -90,6 +91,16 @@ jobs: exit 0; fi + if echo "$PR_TITLE" | grep -E '^update: update '; then + echo "Exception / OK: Dependabot update pattern" + exit 0; + fi + + if echo "$PR_TITLE" | grep -E '^update: bump '; then + echo "Exception / OK: Dependabot update pattern" + exit 0; + fi + echo "Installing commitlint-rs. Please wait 30-40 seconds ..." cargo install --quiet commitlint-rs set -e diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b171cd531..4a1789881 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,9 @@ jobs: name: Release-plz runs-on: ubuntu-latest + outputs: + c2patool-release-tag: ${{ steps.sniff-c2patool-release-tag.outputs.tag }} + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -25,7 +28,8 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: Run release-plz - uses: MarcoIeni/release-plz-action@v0.5.83 + id: release-plz + uses: MarcoIeni/release-plz-action@v0.5.86 env: GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_SECRET }} @@ -38,3 +42,81 @@ jobs: tail -n +2 |\ sed 's/origin\///' |\ xargs -I {} git push origin --delete {} + + - name: Identify c2patool release + id: sniff-c2patool-release-tag + run: | + echo tag=`git tag --contains HEAD | grep '^c2patool-'` >> "$GITHUB_OUTPUT" || true + + publish-c2patool-binaries: + name: Publish c2patool binaries + runs-on: ${{ matrix.os }} + needs: release-plz + + strategy: + fail-fast: false + matrix: + os: [ macos-latest, ubuntu-latest, windows-latest ] + rust_version: [ stable ] + experimental: [ false ] + include: + - os: macos-latest + artifact_name: c2patool_mac_universal.zip + uploaded_asset_name: c2patool-${{ needs.release-plz.outputs.c2patool-release-tag }}-universal-apple-darwin.zip + - os: ubuntu-latest + artifact_name: c2patool_linux_intel.tar.gz + uploaded_asset_name: c2patool-${{ needs.release-plz.outputs.c2patool-release-tag }}-x86_64-unknown-linux-gnu.tar.gz + - os: windows-latest + artifact_name: c2patool_win_intel.zip + uploaded_asset_name: c2patool-${{ needs.release-plz.outputs.c2patool-release-tag }}-x86_64-pc-windows-msvc.zip + + steps: + - name: Checkout repository + if: ${{ needs.release-plz.outputs.c2patool-release-tag }} + uses: actions/checkout@v4 + + - name: Install Rust toolchain + if: ${{ needs.release-plz.outputs.c2patool-release-tag }} + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust_version }} + components: llvm-tools-preview + + - name: Install cargo-sbom + if: ${{ needs.release-plz.outputs.c2patool-release-tag }} + uses: baptiste0928/cargo-install@v3 + with: + crate: cargo-sbom + version: '0.9.1' + + - name: Cache Rust dependencies + if: ${{ needs.release-plz.outputs.c2patool-release-tag }} + uses: Swatinem/rust-cache@v2 + + - name: Run make release + if: ${{ needs.release-plz.outputs.c2patool-release-tag }} + run: cd cli && make release + + - name: Upload binary to GitHub + if: ${{ needs.release-plz.outputs.c2patool-release-tag }} + uses: svenstaro/upload-release-action@v1-release + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: target/${{ matrix.artifact_name }} + asset_name: ${{ matrix.uploaded_asset_name }} + tag: ${{ needs.release-plz.outputs.c2patool-release-tag }} + overwrite: true + + - name: Generate SBOM + if: ${{ needs.release-plz.outputs.c2patool-release-tag }} + run: cd cli && cargo sbom > c2patool.${{ matrix.os }}.sbom.json + + - name: Upload SBOM to Github + if: ${{ needs.release-plz.outputs.c2patool-release-tag }} + uses: svenstaro/upload-release-action@v1-release + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: cli/c2patool.${{ matrix.os }}.sbom.json + asset_name: c2patool-${{ needs.release-plz.outputs.c2patool-release-tag }}-sbom.json + tag: ${{ needs.release-plz.outputs.c2patool-release-tag }} + overwrite: true diff --git a/.gitignore b/.gitignore index d47269fa1..a43edfdfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ /target/ -Cargo.lock - **/*.rs.bk .DS_Store @@ -11,3 +9,6 @@ Cargo.lock .vscode /semver-checks/target/ + +# Unit test output lands here. TO DO: Fix +cli/target diff --git a/CHANGELOG.md b/CHANGELOG.md index d6ee3dd3a..f61183a91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,35 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm Since version 0.36.2, the format of this changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.40.0](https://github.com/contentauth/c2pa-rs/compare/c2pa-v0.39.0...c2pa-v0.40.0) +_12 December 2024_ + +### Added + +* Add `RawSigner` trait to `c2pa-crypto` (derived from `c2pa::Signer`) (#716) +* Move time stamp code into c2pa-crypto (#696) +* Adds ValidationState support (#701) +* Introduce `DynamicAssertion` trait (#566) + +### Fixed + +* Compile `c2pa-crypto` with `cargo check` (#768) +* Verbose assertions for `is_none()` (#704) +* Remove `c2pa::Signer` dependency on `c2pa_crypto::TimeStampProvider` (#718) +* Add support for MP3 without ID3 header (#652) +* Treat Unicode-3.0 license as approved; unpin related dependencies (#693) +* Remote manifest fetch test was not using full path (#675) +* Fix #624 (edge cases when combining the box hashes) (#625) +* Fix #672, Callback is unsound (#674) +* Support "remote_manifest_fetch" verify setting (#667) + +### Updated dependencies + +* Bump chrono from 0.4.38 to 0.4.39 (#763) +* Bump asn1-rs from 0.5.2 to 0.6.2 (#724) +* Bump mockall requirement from 0.11.2 to 0.13.1 in /sdk (#685) +* Update zip requirement from 0.6.6 to 2.2.1 in /sdk (#698) + ## [0.39.0](https://github.com/contentauth/c2pa-rs/compare/c2pa-v0.38.0...c2pa-v0.39.0) _13 November 2024_ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..dcc0bf0a8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4945 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b" +dependencies = [ + "actix-macros", + "actix-rt", + "actix_derive", + "bitflags 2.6.0", + "bytes", + "crossbeam-channel", + "futures-core", + "futures-sink", + "futures-task", + "futures-util", + "log", + "once_cell", + "parking_lot", + "pin-project-lite", + "smallvec", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.90", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix_derive" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6ac1e58cded18cb28ddc17143c4dea5345b3ad575e14f32f66e4054a56eb271" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy 0.7.35", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "assert_cmd" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-generic" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddf3728566eefa873833159754f5732fb0951d3649e6e5b891cc70d56dd41673" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-object-pool" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333c456b97c3f2d50604e8b2624253b7f787208cb72eb75e64b0ad11b221652c" +dependencies = [ + "async-std", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel 2.3.1", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.3.1", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-std" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atree" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "132573478eb9ff973c6f75d0ed425ac12da77d266506483345f46743ecc83a98" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "basic-cookies" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67bd8fd42c16bdb08688243dc5f0cc117a3ca9efeeaba3a345a18a6159ad96f7" +dependencies = [ + "lalrpop", + "lalrpop-util", + "regex", +] + +[[package]] +name = "bcder" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627747a6774aab38beb35990d88309481378558875a41da1a4b2e373c906ef0" +dependencies = [ + "bytes", + "smallvec", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bitvec-nom2" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d988fcc40055ceaa85edc55875a08f8abd29018582647fd82ad6128dba14a5f0" +dependencies = [ + "bitvec", + "nom", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bstr" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "byteordered" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf2cd9424f5ff404aba1959c835cbc448ee8b689b870a9981c76c0fd46280e6" +dependencies = [ + "byteorder", +] + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "c2pa" +version = "0.40.0" +dependencies = [ + "actix", + "anyhow", + "asn1-rs", + "async-generic", + "async-recursion", + "async-trait", + "atree", + "bcder", + "byteorder", + "byteordered", + "bytes", + "c2pa", + "c2pa-crypto", + "c2pa-status-tracker", + "chrono", + "ciborium", + "config", + "console_log", + "conv", + "coset", + "ed25519-dalek", + "extfmt", + "fast-xml", + "getrandom", + "glob", + "hex", + "id3", + "image 0.24.9", + "img-parts", + "jfifdump", + "js-sys", + "jumbf", + "lazy_static", + "log", + "lopdf", + "memchr", + "mockall", + "mp4", + "pem 3.0.4", + "png_pong", + "rand", + "rand_chacha", + "rand_core 0.9.0-beta.1", + "range-set", + "rasn", + "rasn-ocsp", + "rasn-pkix", + "riff", + "rsa", + "schemars", + "serde", + "serde-transcode", + "serde-wasm-bindgen", + "serde_bytes", + "serde_cbor", + "serde_derive", + "serde_json", + "serde_with", + "sha1", + "sha2", + "spki", + "tempfile", + "thiserror 2.0.8", + "tokio", + "treeline", + "ureq", + "url", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", + "x509-certificate", + "x509-parser", + "zip", +] + +[[package]] +name = "c2pa-crypto" +version = "0.2.0" +dependencies = [ + "actix", + "asn1-rs", + "async-generic", + "async-trait", + "base64 0.22.1", + "bcder", + "bytes", + "c2pa-status-tracker", + "chrono", + "ciborium", + "const-hex", + "coset", + "ecdsa", + "ed25519-dalek", + "getrandom", + "hex", + "js-sys", + "nom", + "openssl", + "p256", + "p384", + "rand", + "rasn", + "rasn-ocsp", + "rasn-pkix", + "rsa", + "schemars", + "serde", + "serde_bytes", + "sha1", + "sha2", + "spki", + "thiserror 2.0.8", + "ureq", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-sys", + "web-time", + "x509-certificate", + "x509-parser", +] + +[[package]] +name = "c2pa-status-tracker" +version = "0.2.0" + +[[package]] +name = "c2patool" +version = "0.10.2" +dependencies = [ + "anyhow", + "assert_cmd", + "atree", + "c2pa", + "c2pa-crypto", + "clap", + "env_logger", + "glob", + "httpmock", + "log", + "mockall", + "openssl", + "pem 3.0.4", + "predicates", + "reqwest", + "serde", + "serde_derive", + "serde_json", + "tempfile", + "treeline", + "url", +] + +[[package]] +name = "cawg-identity" +version = "0.1.1" + +[[package]] +name = "cc" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half 2.4.1", +] + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "config" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf" +dependencies = [ + "json5", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", +] + +[[package]] +name = "console_log" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" +dependencies = [ + "log", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "const-hex" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "conv" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" +dependencies = [ + "custom_derive", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "coset" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8cc80f631f8307b887faca24dcc3abc427cd0367f6eb6188f6e8f5b7ad8fb" +dependencies = [ + "ciborium", + "ciborium-io", +] + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "custom_derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.90", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ena" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + +[[package]] +name = "export_schema" +version = "0.36.1" +dependencies = [ + "anyhow", + "c2pa", + "schemars", + "serde_json", +] + +[[package]] +name = "extfmt" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a48fe53466ab1f4ea6303bf9d7a0ca8060778590f09fd6c3304cc817aeb9935d" + +[[package]] +name = "fast-xml" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f7ffc2f9e1373cd82b4d4ce2f84f8162edd48e3932abeb19c06170b66dbbd6c" +dependencies = [ + "memchr", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.2.0", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.2.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "httpmock" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ec9586ee0910472dec1a1f0f8acf52f0fdde93aea74d70d4a3107b4be0fd5b" +dependencies = [ + "assert-json-diff", + "async-object-pool", + "async-std", + "async-trait", + "base64 0.21.7", + "basic-cookies", + "crossbeam-utils", + "form_urlencoded", + "futures-util", + "hyper 0.14.31", + "lazy_static", + "levenshtein", + "log", + "regex", + "serde", + "serde_json", + "serde_regex", + "similar", + "tokio", + "url", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.1", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.5.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.1", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "id3" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55f4e785f2c700217ee82a1c727c720449421742abd5fcb2f1df04e1244760e9" +dependencies = [ + "bitflags 2.6.0", + "byteorder", + "flate2", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-traits", + "png", +] + +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", + "png", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "img-parts" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd653b443fbb9271d937a4b2c1c7489af95c284a56f84d76bbd00eac857cb1c" +dependencies = [ + "bytes", + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jfifdump" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68cf72bc7b75b6615ffd06bfe6840f3c57e7e4ea615558a2a452f458ebf5551e" + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "jumbf" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a32280817bc6e0dbd9aa2abfe52b8fe7405bebc33995649525ecc13ececc3b59" +dependencies = [ + "nom", + "thiserror 1.0.69", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lalrpop" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" +dependencies = [ + "ascii-canvas", + "bit-set", + "ena", + "itertools 0.11.0", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", + "walkdir", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin 0.9.8", +] + +[[package]] +name = "levenshtein" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" + +[[package]] +name = "libc" +version = "0.2.168" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lopdf" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c8e1b6184b1b32ea5f72f572ebdc40e5da1d2921fa469947ff7c480ad1f85a" +dependencies = [ + "chrono", + "encoding_rs", + "flate2", + "itoa", + "linked-hash-map", + "log", + "md5", + "nom", + "rayon", + "time", + "weezl", +] + +[[package]] +name = "make_test_images" +version = "0.36.1" +dependencies = [ + "anyhow", + "c2pa", + "env_logger", + "image 0.25.5", + "log", + "memchr", + "nom", + "regex", + "serde", + "serde_json", + "tempfile", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "mp4" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9ef834d5ed55e494a2ae350220314dc4aacd1c43a9498b00e320e0ea352a5c3" +dependencies = [ + "byteorder", + "bytes", + "num-rational", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.4.1+3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "parsenic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c695d2b8bcf1dd62a5173a9c43e6ebbe9261701c002f9b462fc9690c144bb61" +dependencies = [ + "traitful", +] + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pem" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b13fe415cdf3c8e44518e18a7c95a13431d9bdf6d15367d82b23c377fdd441a" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +dependencies = [ + "memchr", + "thiserror 2.0.8", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "pest_meta" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.7.0", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pix" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6574ffbea793ab0c9a9b09ae99831f1513fdd7732b12aca4c7182c46dc3bc9f" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "png" +version = "0.17.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67582bd5b65bdff614270e2ea89a1cf15bef71245cc1e5f7ea126977144211d" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png_pong" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a767b8b17e388cb7f1e74cf3c2ebce14e6b7ae123fa27150889a353fd9eb981f" +dependencies = [ + "miniz_oxide", + "parsenic", + "pix", + "simd-adler32", + "traitful", +] + +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bitflags 2.6.0", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_core" +version = "0.9.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98fa0b8309344136abe6244130311e76997e546f76fae8054422a7539b43df7" +dependencies = [ + "zerocopy 0.8.13", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "range-set" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714bc4849c399f77ab82177e9b4f012e02f3a7d6191023e016334edde5b21050" +dependencies = [ + "num-traits", + "smallvec", +] + +[[package]] +name = "rasn" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593394cad523914fcac9296bdb69701ad308589b3ea1e16d37b6f0c3cfad075" +dependencies = [ + "bitvec", + "bitvec-nom2", + "bytes", + "chrono", + "either", + "hashbrown 0.14.5", + "nom", + "num-bigint", + "num-integer", + "num-traits", + "once_cell", + "rasn-derive", + "serde_json", + "snafu", +] + +[[package]] +name = "rasn-derive" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f2c367bf1b9a9bc34d40b9c140b52d26b585d0c7755b18496ba061f50639082" +dependencies = [ + "proc-macro2", + "rasn-derive-impl", + "syn 2.0.90", +] + +[[package]] +name = "rasn-derive-impl" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a321099d2f0f08f0afb90e68a16015abea210d4734f7faf6b3497f29feb46a35" +dependencies = [ + "either", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.90", + "uuid", +] + +[[package]] +name = "rasn-ocsp" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe8804910bee097437b7df967160127e250e59b4d497b450c89328cd796ee1f8" +dependencies = [ + "rasn", + "rasn-pkix", +] + +[[package]] +name = "rasn-pkix" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884db2ae0da3a7239daaec802df74f3431062f85651c511e6c40b35a30282b82" +dependencies = [ + "rasn", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.1", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "riff" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c601484456988d75017d86700d3743b949c21cdc7399f940c75e34680d185c5" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags 2.6.0", + "serde", + "serde_derive", +] + +[[package]] +name = "rsa" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "sha2", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rust-ini" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "log", + "once_cell", + "ring 0.17.8", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "schemars" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.90", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-transcode" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590c0e25c2a5bb6e85bf5c1bce768ceb86b316e7a01bdf07d2cb4ec2271990e2" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half 1.8.3", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "indexmap 2.7.0", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" +dependencies = [ + "regex", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" +dependencies = [ + "thiserror-impl 2.0.8", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap 2.7.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "traitful" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d856e22ead1fb79b9fc3cec63300086f680924f2f7b0e2701f6835a28b9c4425" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "treeline" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "value-bag" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d44563646eb934577f2772656c7ad5e9c90fac78aa8013d776fcdaf24625d" +dependencies = [ + "js-sys", + "minicov", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54171416ce73aa0b9c377b51cc3cb542becee1cd678204812e8392e5b0e4a031" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x509-certificate" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5d27c90840e84503cf44364de338794d5d5680bdd1da6272d13f80b0769ee0" +dependencies = [ + "bcder", + "bytes", + "chrono", + "der", + "hex", + "pem 2.0.1", + "ring 0.16.20", + "signature", + "spki", + "thiserror 1.0.69", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67914ab451f3bfd2e69e5e9d2ef3858484e7074d63f204fd166ec391b54de21d" +dependencies = [ + "zerocopy-derive 0.8.13", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7988d73a4303ca289df03316bc490e934accf371af6bc745393cf3c2c5c4f25d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "zip" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "indexmap 2.7.0", + "memchr", + "thiserror 2.0.8", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 3c7d613cb..f54a33c7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,15 @@ resolver = "2" members = [ "cawg_identity", + "cli", "export_schema", "internal/crypto", "internal/status-tracker", "make_test_images", "sdk", ] + +[profile.release] +strip = true # Automatically strip symbols from the binary. +opt-level = 3 +lto = "thin" # Link time optimization. diff --git a/README.md b/README.md index 3b8aadb1a..d39218f24 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,19 @@ 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. The C2PA Rust library is part of the [Content Authenticity Initiative](https://contentauthenticity.org) open-source SDK. -For the best experience, read the docs on the [CAI Open Source SDK documentation website](https://opensource.contentauthenticity.org/docs/c2pa-node/). Some additional documentation for this repository is also available on GitHub: +For the best experience, read the docs on the [CAI Open Source SDK documentation website](https://opensource.contentauthenticity.org/docs/rust-sdk/). Some additional documentation for this repository is also available on GitHub: - [Usage](docs/usage.md) - [Supported formats](docs/supported-formats.md) - [Release notes](docs/release-notes.md) - [Contributing to the project](docs/project-contributions.md) +- [C2PA Tool](cli/README.md) documentation: + - [Using C2PA Tool](cli/docs/usage.md) + - [Manifest definition file](cli/docs/manifest.md) + - [Using an X.509 certificate](cli/docs/x_509.md) + - [Release notes](cli/docs/release-notes.md) + ## Key features @@ -36,6 +42,10 @@ This is a beta release (version 0.x.x) of the project. The minor version number NOTE: The current release includes a new API that replaces old methods of reading and writing C2PA data, which are deprecated. See the [release notes](docs/release-notes.md) for more information. +### Rust language requirement (MSRV) + +The `c2pa` crate requires Rust version 1.81.0 or newer. When a newer version of Rust becomes required, a new minor (0.x.0) version of this crate will be released. + ### Contributions and feedback We welcome contributions to this project. For information on contributing, providing feedback, and about ongoing work, see [Contributing](https://github.com/contentauth/c2pa-rs/blob/main/CONTRIBUTING.md). For additional information on nightly builds and testing, see [Contributing to the project](docs/project-contributions.md). diff --git a/cawg_identity/Cargo.toml b/cawg_identity/Cargo.toml index 390dc76e5..86c6999d3 100644 --- a/cawg_identity/Cargo.toml +++ b/cawg_identity/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" keywords = ["identity"] categories = ["api-bindings"] edition = "2021" -rust-version = "1.76.0" +rust-version = "1.81.0" exclude = ["tests/fixtures"] [package.metadata.docs.rs] diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md new file mode 100644 index 000000000..d88e5c230 --- /dev/null +++ b/cli/CHANGELOG.md @@ -0,0 +1,287 @@ +# Changelog + +All changes to this project are documented in this file. For a high-level summary of changes, see the [Release Notes](../docs/release-notes.md). + +This project adheres to [Semantic Versioning](https://semver.org), except that – as is typical in the Rust community – the minimum supported Rust version may be increased without a major version increase. + +Since version 0.10.0, the format of this changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [0.10.2](https://github.com/contentauth/c2pa-rs/compare/c2patool-v0.10.1...c2patool-v0.10.2) +_12 December 2024_ + +### Fixed + +* No-op change to trigger new c2patool build +* Update makefile for c2patool's new location in c2pa-rs workspace + +## [0.10.1](https://github.com/contentauth/c2pa-rs/compare/c2patool-v0.10.0...c2patool-v0.10.1) +_12 December 2024_ + +### Fixed + +* No-op change to trigger new c2patool release + +## [0.10.0](https://github.com/contentauth/c2pa-rs/compare/c2patool-v0.9.12...c2patool-v0.10.0) +_12 December 2024_ + +### Added + +* Updates c2patool to use only the new Builder/Reader API (contentauth/c2patool#297) + +### Documented + +* Update Contributing guide, misc minor edits (contentauth/c2patool#296) + +### Fixed + +* Compile `c2pa-crypto` with `cargo check` (#768) + +### Other + +* Move c2patool source code into c2pa-rs repo (#723) +* Move profile settings to workspace Cargo.toml +* Enlarged description of c2pa command-line behavior (contentauth/c2patool[#285](https://github.com/contentauth/c2pa-rs/pull/285)) + +## 0.9.12 +_18 October 2024_ + +* fix: Update c2pa-rs for RegionOfInterest support. ([contentauth/c2patool#269](https://github.com/contentauth/c2patool/pull/269)) +* Fix broken link that was causing os site workflow to fail ([contentauth/c2patool#266](https://github.com/contentauth/c2patool/pull/266)) +* Bump codecov/codecov-action from 3 to 4 ([contentauth/c2patool#242](https://github.com/contentauth/c2patool/pull/242)) +* chore: Run all CI jobs when user is dependabot[bot] +* chore: Debug CI again +* chore: Format for consistency with c2pa-rs CI workflow ([contentauth/c2patool#265](https://github.com/contentauth/c2patool/pull/265)) +* chore: Don't skip CI jobs for non-pull-request events +* chore: Retry debug +* chore: Debug action context +* chore: Skip CodeCov upload for non-member PRs ([contentauth/c2patool#263](https://github.com/contentauth/c2patool/pull/263)) +* Bump EmbarkStudios/cargo-deny-action from 1 to 2 ([contentauth/c2patool#245](https://github.com/contentauth/c2patool/pull/245)) +* chore: Adjust conditions for running CI jobs ([contentauth/c2patool#261](https://github.com/contentauth/c2patool/pull/261)) + +## 0.9.11 +_16 October 2024_ + +* Merge hardening bug fixes ([contentauth/c2patool#260](https://github.com/contentauth/c2patool/pull/260)) + +## 0.9.10 +_07 October 2024_ + +* Update c2ptool to use latest c2pa-rs ([contentauth/c2patool#258](https://github.com/contentauth/c2patool/pull/258)) + +## 0.9.9 +_17 September 2024_ + +* Pull in latest bug fixes ([contentauth/c2patool#237](https://github.com/contentauth/c2patool/pull/237)) +* Document fragment subcommand ([contentauth/c2patool#236](https://github.com/contentauth/c2patool/pull/236)) +* Switch back to using `pull_request` instead of `pull_request_target` trigger +* Bump actions/checkout from 3 to 4 ([contentauth/c2patool#243](https://github.com/contentauth/c2patool/pull/243)) +* Remove no-longer-maintained clippy-check action ([contentauth/c2patool#238](https://github.com/contentauth/c2patool/pull/238)) + +## 0.9.8 +_30 August 2024_ + +* Initial fragment support ([contentauth/c2patool#230](https://github.com/contentauth/c2patool/pull/230)) +* Add warning about accessing a private key directly ([contentauth/c2patool#218](https://github.com/contentauth/c2patool/pull/218)) + +## 0.9.7 +_15 August 2024_ + +* Update to latest c2pa SDK ([contentauth/c2patool#222](https://github.com/contentauth/c2patool/pull/222)) +* Remove rust toolchain version lock ([contentauth/c2patool#221](https://github.com/contentauth/c2patool/pull/221)) +* Update security guidance to link to SECURITY.md ([contentauth/c2patool#217](https://github.com/contentauth/c2patool/pull/217)) + +## 0.9.6 +_30 July 2024_ + +* Pull latest c2pa-rs bug fixes into c2patool ([contentauth/c2patool#212](https://github.com/contentauth/c2patool/pull/212)) +* only run tests/clippy if labeled ([contentauth/c2patool#205](https://github.com/contentauth/c2patool/pull/205)) +* Bump env_logger from 0.10.2 to 0.11.4 ([contentauth/c2patool#204](https://github.com/contentauth/c2patool/pull/204)) +* Updates cargo packages and cargo.deny file. ([contentauth/c2patool#200](https://github.com/contentauth/c2patool/pull/200)) + +## 0.9.5 +_18 July 2024_ + +* Update to lastest c2pa-rs ([contentauth/c2patool#197](https://github.com/contentauth/c2patool/pull/197)) +* added security.md ([contentauth/c2patool#196](https://github.com/contentauth/c2patool/pull/196)) + +## 0.9.4 +_25 June 2024_ + +* Update c2patool ([contentauth/c2patool#190](https://github.com/contentauth/c2patool/pull/190)) +* Match c2pa-rs minimum toolchain version and test in CI ([contentauth/c2patool#188](https://github.com/contentauth/c2patool/pull/188)) +* Document how to specify an icon ([contentauth/c2patool#182](https://github.com/contentauth/c2patool/pull/182)) + +## 0.9.3 +_29 May 2024_ + +* Remove binary modules ([contentauth/c2patool#179](https://github.com/contentauth/c2patool/pull/179)) + +## 0.9.2 +_24 May 2024_ + +* Remove integration tests for now due to extraneous binaries ([contentauth/c2patool#178](https://github.com/contentauth/c2patool/pull/178)) + +## 0.9.1 +_22 May 2024_ + +* Add better support for cargo-binstall ([contentauth/c2patool#177](https://github.com/contentauth/c2patool/pull/177)) + +## 0.9.0 +_07 May 2024_ + +* Integrate with c2pa-rs 0.32.0, various test case fixes. ([contentauth/c2patool#175](https://github.com/contentauth/c2patool/pull/175)) +* Add HTTP source option for trust config ([contentauth/c2patool#174](https://github.com/contentauth/c2patool/pull/174)) + +## 0.8.2 +_28 March 2024_ + +* fixed c2patool asset name ([contentauth/c2patool#171](https://github.com/contentauth/c2patool/pull/171)) + +## 0.8.1 +_25 March 2024_ + +* use c2pa-rs 0.31.1 for actions.changes support ([contentauth/c2patool#170](https://github.com/contentauth/c2patool/pull/170)) + +## 0.8.0 +_20 March 2024_ + +* allow clients to sign with a process outside of c2patool ([contentauth/c2patool#169](https://github.com/contentauth/c2patool/pull/169)) +* Add trust and verification options to c2pa_tool ([contentauth/c2patool#168](https://github.com/contentauth/c2patool/pull/168)) +* adds version to c2patool artifact names ([contentauth/c2patool#158](https://github.com/contentauth/c2patool/pull/158)) + +## 0.7.0 +_22 November 2023_ + +* updates to c2pa-rs v0.28.2 ([contentauth/c2patool#153](https://github.com/contentauth/c2patool/pull/153)) +* Update to c2pa-rs 0.28.1 + +## 0.6.2 +_05 October 2023_ + +* update to c2pa 0.27.1 ([contentauth/c2patool#146](https://github.com/contentauth/c2patool/pull/146)) +* Merge branch 'main' of https://github.com/contentauth/c2patool +* Add Do not train example +* Upgrade to c2pa-rs 0.26.0 ([contentauth/c2patool#143](https://github.com/contentauth/c2patool/pull/143)) +* Fix issue with docusaurus styling and fix broken links ([contentauth/c2patool#138](https://github.com/contentauth/c2patool/pull/138)) +* Updates to c2pa-rs 0.25.1 ([contentauth/c2patool#128](https://github.com/contentauth/c2patool/pull/128)) +* Fix windows release ([contentauth/c2patool#132](https://github.com/contentauth/c2patool/pull/132)) + +## 0.6.1 +_24 July 2023_ + +* use compress-archive instead of tar ([contentauth/c2patool#130](https://github.com/contentauth/c2patool/pull/130)) + +## 0.6.0 +_22 June 2023_ + +* update to c2pa-rs 0.24.0 ([contentauth/c2patool#127](https://github.com/contentauth/c2patool/pull/127)) + +## 0.5.4 +_13 June 2023_ + +* integrate c2pa 23.0 bump version ([contentauth/c2patool#126](https://github.com/contentauth/c2patool/pull/126)) +* Merge branch 'main' of https://github.com/contentauth/c2patool +* c2pa-rs 23.0 + updated test +* Update README.md ([contentauth/c2patool#124](https://github.com/contentauth/c2patool/pull/124)) + +## 0.5.3 +_04 May 2023_ + +* Parent Ingredient JSON ([contentauth/c2patool#123](https://github.com/contentauth/c2patool/pull/123)) + +## 0.5.2 +_19 April 2023_ + +* Ingredient thumbnails, extension cleanup, toolkit update ([contentauth/c2patool#120](https://github.com/contentauth/c2patool/pull/120)) + +## 0.5.1 +_10 April 2023_ + +* Update README.md ([contentauth/c2patool#118](https://github.com/contentauth/c2patool/pull/118)) +* Update expired sample certs ([contentauth/c2patool#113](https://github.com/contentauth/c2patool/pull/113)) + +## 0.5.0 +_28 March 2023_ + +* New ingredient support and c2pa file formats ([contentauth/c2patool#111](https://github.com/contentauth/c2patool/pull/111)) +* Leverage new Manifest & Ingredient, add Ingredient creation. ([contentauth/c2patool#107](https://github.com/contentauth/c2patool/pull/107)) + +## 0.4.0 +_01 March 2023_ + +* Add --certs and --tree options ([contentauth/c2patool#106](https://github.com/contentauth/c2patool/pull/106)) +* update to cp2pa 0.17.0 ([contentauth/c2patool#105](https://github.com/contentauth/c2patool/pull/105)) +* Update for Clippy in Rust 1.67 ([contentauth/c2patool#101](https://github.com/contentauth/c2patool/pull/101)) + +## 0.3.9 +_06 December 2022_ + +* update to c2pa-rs 0.16.0 +* allows clients to output manifest report to specified directory ([contentauth/c2patool#91](https://github.com/contentauth/c2patool/pull/91)) + +## 0.3.8 +_09 November 2022_ + +* Bump c2pa from 0.13.2 to 0.15.0 ([contentauth/c2patool#87](https://github.com/contentauth/c2patool/pull/87)) +* Build infrastructure improvements ([contentauth/c2patool#85](https://github.com/contentauth/c2patool/pull/85)) +* Fix new Clippy warning in Rust 1.65 ([contentauth/c2patool#84](https://github.com/contentauth/c2patool/pull/84)) +* Readme updates ([contentauth/c2patool#62](https://github.com/contentauth/c2patool/pull/62)) + +## 0.3.7 +_22 September 2022_ + +* Treat a source asset with a manifest store as a default parent ([contentauth/c2patool#76](https://github.com/contentauth/c2patool/pull/76)) +* Fetch remote manifests for --info ([contentauth/c2patool#75](https://github.com/contentauth/c2patool/pull/75)) + +## 0.3.6 +_16 September 2022_ + +* Update Cargo.lock when publishing crate ([contentauth/c2patool#71](https://github.com/contentauth/c2patool/pull/71)) +* [IGNORE] update readme --info ([contentauth/c2patool#70](https://github.com/contentauth/c2patool/pull/70)) +* Update Cargo.lock to 0.3.5 + +## 0.3.5 +_15 September 2022_ + +* Upgrade cpufeatures to non-yanked version ([contentauth/c2patool#68](https://github.com/contentauth/c2patool/pull/68)) +* Add --info option ([contentauth/c2patool#65](https://github.com/contentauth/c2patool/pull/65)) +* Updated publish workflow to upload binaries to GitHub ([contentauth/c2patool#58](https://github.com/contentauth/c2patool/pull/58)) +* Fix Make release script & update readme ([contentauth/c2patool#55](https://github.com/contentauth/c2patool/pull/55)) +* (Some version history omitted as we worked on some release process issues) + +## 0.3.0 +_18 August 2022_ + +* Rework c2patool parameters ([contentauth/c2patool#53](https://github.com/contentauth/c2patool/pull/53)) +* Update to 0.11.0 c2pa-rs ([contentauth/c2patool#38](https://github.com/contentauth/c2patool/pull/38)) +* Remove Homebrew, Git installation methods, and add "update" wording ([contentauth/c2patool#33](https://github.com/contentauth/c2patool/pull/33)) + +## 0.2.1 +_29 June 2022_ + +* Add BMFF support for video & etc ([contentauth/c2patool#25](https://github.com/contentauth/c2patool/pull/25)) + +## 0.2.0 +_28 June 2022_ + +* Upgrade to c2pa Rust SDK version 0.6.0 ([contentauth/c2patool#24](https://github.com/contentauth/c2patool/pull/24)) +* Fix an error in the README documentation ([contentauth/c2patool#23](https://github.com/contentauth/c2patool/pull/23)) +* Display help if there are no arguments on the command line ([contentauth/c2patool#21](https://github.com/contentauth/c2patool/pull/21)) +* Bump anyhow from 1.0.57 to 1.0.58 ([contentauth/c2patool#17](https://github.com/contentauth/c2patool/pull/17)) +* Updates examples to use ta_url instead of ta ([contentauth/c2patool#15](https://github.com/contentauth/c2patool/pull/15)) + +## 0.1.3 +_17 June 2022_ + +* Update to latest c2pa Rust SDK ([contentauth/c2patool#12](https://github.com/contentauth/c2patool/pull/12)) +* Add built-in default certs to make getting started easier ([contentauth/c2patool#9](https://github.com/contentauth/c2patool/pull/9)) + +## 0.1.2 +_10 June 2022_ + +* Update crate's description field + +## 0.1.1 +_10 June 2022_ + +* Initial public release diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 000000000..4a9b6f8db --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "c2patool" +default-run = "c2patool" + +version = "0.10.2" + +description = "Tool for displaying and creating C2PA manifests." +authors = [ + "Gavin Peacock ", + "Maurice Fisher ", +] +license = "MIT OR Apache-2.0" +documentation = "https://opensource.contentauthenticity.org/docs/c2patool" +readme = "README.md" +keywords = ["c2pa", "xmp", "metadata"] +edition = "2018" +homepage = "https://contentauthenticity.org" +repository = "https://github.com/contentauth/c2pa-rs/tree/main/cli" + +[dependencies] +anyhow = "1.0" +atree = "0.5.2" +c2pa = { path = "../sdk", version = "0.40.0", features = [ + "fetch_remote_manifests", + "file_io", + "add_thumbnails", + "pdf", + "unstable_api", +] } +c2pa-crypto = { path = "../internal/crypto", version = "0.2.0" } +clap = { version = "4.5.10", features = ["derive", "env"] } +env_logger = "0.11.4" +glob = "0.3.1" +log = "0.4" +serde = { version = "1.0", features = ["derive"] } +serde_derive = "1.0" +serde_json = "1.0" +tempfile = "3.3" +treeline = "0.1.0" +pem = "3.0.3" +openssl = { version = "0.10.61", features = ["vendored"] } +reqwest = { version = "0.12.4", features = ["blocking"] } +url = "2.5.0" + +[dev-dependencies] +assert_cmd = "2.0.14" +httpmock = "0.7.0" +predicates = "3.1" +mockall = "0.13.0" + +[package.metadata.binstall] +# Use defaults diff --git a/cli/Makefile b/cli/Makefile new file mode 100644 index 000000000..b73138f22 --- /dev/null +++ b/cli/Makefile @@ -0,0 +1,72 @@ +# Makefile to aid with local development and testing +# This is not required for automated builds + +ifeq ($(OS),Windows_NT) + PLATFORM := win +else + UNAME := $(shell uname) + ifeq ($(UNAME),Linux) + PLATFORM := linux + endif + ifeq ($(UNAME),Darwin) + PLATFORM := mac + endif +endif + +check-format: + cargo +nightly fmt -- --check + +clippy: + cargo clippy --all-features --all-targets -- -D warnings + +test-local: + cargo test --all-features + +# Full local validation, build and test all features including wasm +# Run this before pushing a PR to pre-validate +test: check-format clippy test-local + +fmt: + cargo +nightly fmt + +# 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 README.md ../target/c2patool/README.md + cp sample/* ../target/c2patool/sample + cp CHANGELOG.md ../target/c2patool/CHANGELOG.md + +# 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) +release: build-release-mac-universal c2patool-package + cd ../target && zip -r c2patool_mac_universal.zip c2patool && cd .. +endif +ifeq ($(PLATFORM), win) +release: build-release-win c2patool-package + cd ../target && 7z a -r c2patool_win_intel.zip c2patool && cd .. +endif +ifeq ($(PLATFORM), linux) +release: build-release-linux c2patool-package + cd ../target && tar -czvf c2patool_linux_intel.tar.gz c2patool && cd .. +endif diff --git a/cli/README.md b/cli/README.md new file mode 100644 index 000000000..d48b6efaf --- /dev/null +++ b/cli/README.md @@ -0,0 +1,78 @@ +# C2PA command line tool + +C2PA Tool, `c2patool`, is a command line tool for working with C2PA [manifests](https://c2pa.org/specifications/specifications/1.4/specs/C2PA_Specification.html#_manifests) and media assets (audio, image or video files). + +Use the tool on a file in one of the [supported formats](https://github.com/contentauth/c2pa-rs/blob/main/docs/supported-formats.md) to: + +- Read a summary JSON report of C2PA manifests. +- Read a low-level report of C2PA manifest data. +- Add a C2PA manifest to the file. + +For a simple example of calling c2patool from a Node.js server application, see the [c2pa-service-example](https://github.com/contentauth/c2patool-service-example) repository. + +
+ +**Additional documentation**: + +- [Using C2PA Tool](./docs/usage.md) +- [Manifest definition file](./docs/manifest.md) +- [Using an X.509 certificate](./docs/x_509.md) +- [Release notes](./docs/release-notes.md) +- [Contributing to the project](./docs/project-contributions.md) + +
+ +## Installation + +There are two ways to install C2PA Tool: +- [Using a pre-built binary executable](#installing-a-pre-built-binary): This is the quickest way to install the tool. If you just want to try C2PA Tool quickly, use this method. +- [Using Cargo Binstall](#using-cargo-binstall), a low-complexity way to install Rust binaries. This method is preferable for long-term use. If you know you want to use C2PA Tool for development, use this method. + +**NOTE:** If you want to contribute to the C2PA Tool project itself, or if a pre-built binary is not available for your system, see [Contributing to the project](./docs/project-contributions.md). + +### Installing a pre-built binary + +The quickest way to install the tool is to use the binary executable builds. If you just want to try C2PA Tool quickly: + +1. Go to the [Releases page](https://github.com/contentauth/c2pa-rs/releases). +1. Under the latest **c2patool** release, click **Assets**. +1. Download the archive for your operating system (Linux, macOS, or Windows). +1. Copy the executable file to a location on your `PATH`. + +Confirm that you can run the tool by entering a command such as: +``` +c2patool -h +``` + +NOTE: You also may want to get some of the example files provided in the repository `sample` directory. To do so, clone the repository with `git clone https://github.com/contentauth/c2pa-rs.git`. + +### Using Cargo Binstall + +Installing C2PA Tool using Cargo [Binstall](https://github.com/cargo-bins/cargo-binstall?tab=readme-ov-file) is recommended because it makes it easier to: +- Automatically select the correct installation package for your platform/architecture. +- Update the tool when a new version is released. +- Maintain, since you don't have to manually keep track of random binaries on your system. +- Integrate into CI or other scripting environments. + +Additionally, using Binstall enables you to automate code signing to ensure package integrity. + +#### Process + +**PREREQUISITE:** Install [Rust](https://www.rust-lang.org/tools/install). + +To install by using Binstall: + +1. Install `cargo-binstall` by following the [quick install method](https://github.com/cargo-bins/cargo-binstall?tab=readme-ov-file#quickly) for your OS, or by building from source by running `cargo install cargo-binstall` +2. Run `cargo binstall c2patool`. + +#### Upgrading + +To ensure you have the latest version, enter this command: + +``` +c2patool -V +``` + +The tool will display the version installed. Compare the version number displayed with the latest release version shown in the [repository releases page](https://github.com/contentauth/c2pa-rs/releases). + +If you need to upgrade, simply run `cargo binstall c2patool` again, or use [cargo-update](https://github.com/nabijaczleweli/cargo-update). diff --git a/cli/docs/.gitkeep b/cli/docs/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cli/docs/manifest.md b/cli/docs/manifest.md new file mode 100644 index 000000000..95d6a1a17 --- /dev/null +++ b/cli/docs/manifest.md @@ -0,0 +1,85 @@ +# Manifest definition file + +C2PA Tool reads a manifest definition JSON file with a `.json` extension. This file defines a single manifest to be added to an asset's manifest store. +In the manifest definition file, file paths are relative to the location of the file unless you specify a `base_path` field. + +## JSON format + +The C2PA specification describes a manifest that has a binary structure in JPEG universal metadata box format (JUMBF). However, C2PA Tool works with a JSON manifest structure that's easier to understand and work with. It's a declarative language for representing and creating a manifest in binary format. For more information on the JSON manifest, see [Working with manifests](https://opensource.contentauthenticity.org/docs/manifest/understanding-manifest). + +See also: + +* Manifest store reference +* Manifest store schema +* Manifest definition schema +* Ingredient schema + +## Adding a claim generator icon + +You can specify an icon to be displayed by tools such as [Verify](https://contentcredentials.org/verify) to indicate the signer of the manifest. + +To do this, add a `claim_generator_info` property to the manifest definition. The `claim_generator_info.icon` property contains information on the icon: +- `icon.format` specifies the MIME type of the icon file. SVG format is preferred, but you can also use PNG or JPEG formats. +- `icon.identifier` specifies the name of the icon file. + +For example: + +```json +"claim_generator_info": [ + { + "name": "My App", + "version": "0.1.0", + "icon": { + "format": "image/svg+xml", + "identifier": "logo.svg" + } + } +], +``` + +To add the icon using C2PA Tool, make sure the icon file and the manifest definition file are in the same directory where you are running `c2patool`. Then, you can add the icon by using a command like this: + +```shell +c2patool image_to_sign.jpg -m manifest.json -o signed_with_icon.jpg +``` + +NOTE: The [Verify](https://contentcredentials.org/verify) tool will not display an icon for a signing certificate that is not on the temporary certificate list, such as the C2PA Tool test certificate. + +## Example + +The example below is a snippet of a manifest definition that inserts a `CreativeWork` author assertion. This example uses the default testing certificates in the [sample folder](https://github.com/contentauth/c2pa-rs/tree/main/cli/sample) that are also built into the `c2patool` binary. + +**NOTE**: When you don't specify a key or certificate in the manifest `private_key` and `sign_cert` fields, the tool will use the built-in key and cert. You'll see a warning message, since they are meant for development purposes only. For actual use, provide a permanent key and certificate in the manifest definition or environment variables; see [Creating and using an X.509 certificate](x_509.md). + +The following manifest properties are specific to c2patool and used for signing manifests: + +- `alg`: Signing algorithm to use. See [Creating and using an X.509 certificate](x_509.md) for possible values. Default: `es256`. +- `private_key`: Private key to use. Default: `es256_private.key` +- `sign_cert`: Signing certificate to use. Default: `es256_certs.pem` +- `ta_url`: Time Authority URL for getting a time-stamp (for example, `http://timestamp.digicert.com`). A time-stamp provides a way to confirm that the manifest was signed when the certificate was valid, even if the certificate has since expired. Howver, the Time Authority URL requires a live online connection for confirmation, which may not always be available. + +```json +{ + "alg": "es256", + "private_key": "es256_private.key", + "sign_cert": "es256_certs.pem", + "ta_url": "http://timestamp.digicert.com", + + "claim_generator": "TestApp", + "assertions": [ + { + "label": "stds.schema-org.CreativeWork", + "data": { + "@context": "https://schema.org", + "@type": "CreativeWork", + "author": [ + { + "@type": "Person", + "name": "Joe Bloggs" + } + ] + } + } + ] +} +``` diff --git a/cli/docs/nightly-builds/README.md b/cli/docs/nightly-builds/README.md new file mode 100644 index 000000000..a045d1338 --- /dev/null +++ b/cli/docs/nightly-builds/README.md @@ -0,0 +1,32 @@ +# About c2patool nightly builds + +Interim binaries are generated every day around 0530 UTC (i.e. overnight for our US-based team) and are available for roughly two weeks thereafter. These can be helpful for testing purposes. + +## Finding nightly builds + +Start by clicking on the [Actions](https://github.com/contentauth/c2patool/actions) tab for the repo and then click on the ["Nightly build" entry on the left side](https://github.com/contentauth/c2patool/actions/workflows/nightly.yml). + +In this screen, you'll see a history of recent nightly builds, something like this: + +![Nightly build runs](./nightly-build-runs.png) + +Click on the build description (for example, "Use v2 of rust-cache" in this list). That will lead to a summary screen similar to the following: + +![Nightly build summary](./nightly-build-summary.png) + +In the **"Artifacts"** section at the bottom are archives containing the `c2patool` binary and relevant sample files. + +Note that each build contains a unique build number. If you file a bug against a nightly version, we would very much appreciate it if you would include that version ID: + +``` +$ c2patool --version +c2patool 0.6.2-nightly+2023-08-29-24601f5 +``` + +This build number contains the date of the nightly version and the commit ID (from `main` branch) that was built. + +## Important notes about nightly builds + +* CAI team members may occasionally trigger "nightly" builds at other times of the day if there are particularly interesting changes that we need to test. +* Nightly builds of `c2patool` are built with the latest available nightly build of `c2pa-rs`. Typically this is a snapshot of `c2pa-rs` taken at 0500 UTC (i.e. very shortly before the corresponding `c2patool` build). +* This repo contains a `nightly` branch which contains the code for the latest build that was triggered (typically the Cargo.toml and Cargo.lock changes). This branch is force-pushed by the nightly build process; you should not rely on its contents and should not target this build for any pull requests. diff --git a/cli/docs/nightly-builds/nightly-build-runs.png b/cli/docs/nightly-builds/nightly-build-runs.png new file mode 100644 index 000000000..824f61a84 Binary files /dev/null and b/cli/docs/nightly-builds/nightly-build-runs.png differ diff --git a/cli/docs/nightly-builds/nightly-build-summary.png b/cli/docs/nightly-builds/nightly-build-summary.png new file mode 100644 index 000000000..caa784d84 Binary files /dev/null and b/cli/docs/nightly-builds/nightly-build-summary.png differ diff --git a/cli/docs/project-contributions.md b/cli/docs/project-contributions.md new file mode 100644 index 000000000..940e80cf5 --- /dev/null +++ b/cli/docs/project-contributions.md @@ -0,0 +1,23 @@ +# Contributing + +The information in this page is primarily for those who wish to contribute to the c2patool project itself, rather than those who simply wish to use it as a tool. For general contribution guidelines, see [CONTRIBUTING.md](../CONTRIBUTING.md). + +## Building from source + +To build the project from source, enter these commands: + +```shell +cargo install c2patool +``` + +To build the tool on a Windows machine, you need to install the [7zip](https://www.7-zip.org/) tool. + +NOTE: If you encounter errors installing, you may need to update your Rust installation by entering this command: + +``` +rustup update +``` + +## Nightly builds + +Interim binaries are generated every day around 05:30 UTC (overnight for our US-based team) and are available for roughly two weeks thereafter. These can be helpful for testing purposes. For more information, see the documentation on [nightly builds](https://github.com/contentauth/c2patool/tree/main/docs/nightly-builds/README.md). diff --git a/cli/docs/release-notes.md b/cli/docs/release-notes.md new file mode 100644 index 000000000..9dec0b442 --- /dev/null +++ b/cli/docs/release-notes.md @@ -0,0 +1,74 @@ +# C2PA Tool release notes + +This page highlights noteworthy changes in each release of the `c2patool` CLI. + +Refer to the [CHANGELOG](https://github.com/contentauth/c2pa-rs/blob/main/cli/CHANGELOG.md) for detailed Git changes. + +## 0.9.x + +* Update c2pa-rs for RegionOfInterest support. ([#269](https://github.com/contentauth/c2patool/pull/269)) +* Fix broken link that was causing os site workflow to fail ([#266](https://github.com/contentauth/c2patool/pull/266)) +* Document fragment subcommand ([#236](https://github.com/contentauth/c2patool/pull/236)) +* Initial fragment support ([#230](https://github.com/contentauth/c2patool/pull/230)) +* Add warning about accessing a private key directly ([#218](https://github.com/contentauth/c2patool/pull/218)) +* Update c2patool with c2pa-rs fixes ([#190](https://github.com/contentauth/c2patool/pull/190)): + - Merkle trees with empty proofs + - Support for missing metadata for Claims object + - OCSP fixes +* Document how to specify an icon ([#182](https://github.com/contentauth/c2patool/pull/182)) +* Add better support for cargo-binstall ([#177](https://github.com/contentauth/c2patool/pull/177)) +* Add HTTP source option for trust config ([#174](https://github.com/contentauth/c2patool/pull/174)) + +## 0.8.x + +* fixed c2patool asset name ([#171](https://github.com/contentauth/c2patool/pull/171)) +* Allow clients to sign with a process outside of c2patool ([#169](https://github.com/contentauth/c2patool/pull/169)) +* Add trust and verification options to c2pa_tool ([#168](https://github.com/contentauth/c2patool/pull/168)) +* Add version to c2patool artifact names ([#158](https://github.com/contentauth/c2patool/pull/158)) + +## 0.7.x + +* Update to c2pa-rs v0.28.2 ([#153](https://github.com/contentauth/c2patool/pull/153)) +* Fix windows release ([#132](https://github.com/contentauth/c2patool/pull/132)) +* Use compress-archive instead of tar ([#130](https://github.com/contentauth/c2patool/pull/130)) + +## 0.6.0 + +* Validates 1.3 signatures but will not generate them. +* Supports other 1.3 features such as actions v2 and ingredients v2. +* Supports adding `claim_generator_info` to a manifest. +* Icons for `claim_generator_info` can be added as resource references. +* The SDK will create v2 actions or ingredients if required, but defaults to v1. + +## 0.5.4 + +NOTE: This release introduced a 1.3 required change in signature format that is not compatible with previous verify code. +We want to give some time for developers to integrate 1.3 validation before using 1.3 signatures. +Please avoid using 0.5.4 and update to 0.6.0 which can validate the new format but does not create it. + +## 0.5.3 + +* Fixes a bug where ingredient thumbnails were not generated. +* You can now pass an `ingredient.json` file or folder using the command line `--parent` option. If a folder is passed as an ingredient, the tool will look for an ingredient.json fle in that folder. +* Fixes `--parent` is no longer relative to the `--manifest` path + +## 0.5.2 + +* Removes manifest preview feature +* Tests for similar extensions +* Adds `.svg` support + +## 0.5.1 + +* Updates the sample certs which had expired +* Updates to the README for 0.5.0 changes + +## 0.5.0 + +* Adds support for many new file formats, see [supported file formats](https://opensource.contentauthenticity.org/docs/c2patool/#supported-file-formats). +* Manifests and Ingredients can read and write thumbnail and c2pa resource files. +* Adds `-i/--ingredient` option to generate an ingredient report or folder. +* Changes to Manifest Definition: + * `ingredients` now requires JSON Ingredient definitions. + * `ingredient_paths` accepts file paths, including JSON Ingredient definitions. + * `base_path` no longer supported. File paths are relative to the containing JSON file. diff --git a/cli/docs/usage.md b/cli/docs/usage.md new file mode 100644 index 000000000..de99e8f7f --- /dev/null +++ b/cli/docs/usage.md @@ -0,0 +1,292 @@ +# Using C2PA Tool + +C2PA Tool's command-line syntax is: + +``` +c2patool [OPTIONS] [COMMAND] +``` + +Where: +- `OPTIONS` is one or more of the command-line options described in following table. +- `` is the (relative or absolute) file path to the asset to read or embed a manifest into. +- `[COMMAND]` is one of the optional subcommands: `trust`, `fragment`, or `help`. + +By default, c2patool writes a JSON representation of C2PA manifests found in the asset to the standard output. + +## Subcommands + +The tool supports the following subcommands: +- `trust` [configures trust support](#configuring-trust-support) for certificates on a "known certificate list." With this subcommand, several additional options are available. +- `fragment` [adds a manifest to fragmented BMFF content](#adding-a-manifest-to-fragmented-bmff-content). With this subcommand, one additional option is available. +- `help` displays command line help information. + +## Options + +The following options are available with any (or no) subcommand. Additional options are available with each subcommand. + +| CLI option          | Short version | Argument | Description | +|-----|----|----|----| +| `--certs` | | N/A | Extract a certificate chain to standard output (stdout). | +| `--config` | `-c` | `` | Specify a manifest definition as a JSON string. See [Providing a manifest definition on the command line](#providing-a-manifest-definition-on-the-command-line). | +| `--detailed` | `-d` | N/A | Display detailed C2PA-formatted manifest data. See [Displaying a detailed manifest report](#detailed-manifest-report). | +| `--force` | `-f` | N/A | Force overwriting output file. See [Forced overwrite](#forced-overwrite). | +| `--help` | `-h` | N/A | Display CLI help information. | +| `--info` | | N/A | Display brief information about the file. | +| `--ingredient` | `-i` | N/A | Create an Ingredient definition in --output folder. | +| `--output` | `-o` | `` | Path to output folder or file. See [Adding a manifest to an asset file](#adding-a-manifest-to-an-asset-file). | +| `--manifest` | `-m` | `` | Specify a manifest file to add to an asset file. See [Adding a manifest to an asset file](#adding-a-manifest-to-an-asset-file). +| `--no_signing_verify` | None | N/A | Do not validate the signature after signing an asset, which speeds up signing. See [Speeding up signing](#speeding-up-signing) | +| `--parent` | `-p` | `` | Path to parent file. See [Specifying a parent file](#specifying-a-parent-file). | +| `--remote` | `-r` | `` | URL for remote manifest available over HTTP. See [Generating a remote manifest](#generating-a-remote-manifest)| N/A? | +| `--reserve-size` | N/A | Only valid with `--signer-path` argument. The amount of memory to reserve for signing. Default: 20000. For more information, see CLI help. | +| `--sidecar` | `-s` | N/A | Put manifest in external "sidecar" file with `.c2pa` extension. See [Generating an external manifest](#generating-an-external-manifest). | +| `--signer-path` | N/A | Specify path to command-line executable for signing. See [Signing claim bytes with your own signer](#signing-claim-bytes-with-your-own-signer). | +| `--tree` | | N/A | Create a tree diagram of the manifest store. | +| `--version` | `-V` | N/A | Display version information. | + +## Displaying manifest data + +To display the manifest associated with an asset file, provide the path to the file as the argument; for example: + +```shell +c2patool sample/C.jpg +``` + +The tool displays the manifest JSON to standard output (stdout). + +Use the `--output` argument to write the contents of the manifest, (including the manifest's assertion and ingredient thumbnails) to the specified directory. + +```shell +c2patool sample/C.jpg --output ./report +``` + +### Detailed manifest report + +Use the `-d` option to display a detailed report describing the internal C2PA format of manifests contained in the asset; for example, using one of the example images in the `sample` directory: + +```shell +c2patool sample/C.jpg -d +``` + +By default, the tool displays the detailed report to standard output (stdout). If you specify an output folder, the tool saves it to a file named `detailed.json` in that folder. + +### Displaying an information report + +Use the `--info` option to print a high-level report about the asset file and related C2PA data. +For a cloud manifest the tool displays the URL to the manifest. +Displays the size of the manifest store and number of manifests. +It will report if the manifest validated or show any errors encountered in validation. + + +```shell +c2patool sample/C.jpg --info +``` + +The tool displays the report to standard output (stdout). + +## Creating an ingredient from a file + +The `--ingredient` option creates an ingredient report. When used with the `--output` folder, it extracts or creates a thumbnail image and a binary `.c2pa` manifest store containing the C2PA data from the file. The JSON ingredient this produces can be added to a manifest definition to carry the full history and validation record of that asset into a newly-created manifest. + +Provide the path to the file as the argument; for example: + +```shell +c2patool sample/C.jpg --ingredient --output ./ingredient +``` + +## Adding a manifest to an asset file + +Use the `--manifest` / `-m` option to add the C2PA manifest definition file specified in the argument to the asset file to be signed. Specify the output file as the argument to the `--output` / `-o` option. The output extension type must match the source. The tool will not convert between file types. For example: + +```shell +c2patool sample/image.jpg -m sample/test.json -o signed_image.jpg +``` + +The tool generates a new manifest using the values given in the file and displays the manifest store to standard output (stdout). + +CAUTION: If the output file is the same as the source file, the tool will overwrite the source file. + +If the manifest definition file has `private_key` and `sign_cert` fields, then the tool signs the manifest using the private key and certificate they specify, respectively. Otherwise, the tool uses the built-in test certificate and key, which is suitable ONLY for development and testing. You can also specify the private key and certificate using environment variables; for more information, see [Creating and using an X.509 certificate](x_509.md). + +**WARNING**: Accessing the private key and signing certificate directly like this is fine during development, but doing so in production may be insecure. Instead use a Key Management Service (KMS) or a hardware security module (HSM) to access the certificate and key; for example as show in the [C2PA Python Example](https://github.com/contentauth/c2pa-python-example). + +### Specifying a parent file + +A _parent file_ represents the state of the image before the current edits were made. + +Specify a parent file as the argument to the `--parent` / `-p` option; for example: + +```shell +c2patool sample/image.jpg -m sample/test.json -p sample/c.jpg -o signed_image.jpg +``` + +You can pass an ingredient generated with the `--ingredient` option by giving the folder or ingredient JSON file. + +```shell +c2patool sample/C.jpg --ingredient --output ./ingredient + +c2patool sample/image.jpg -m sample/test.json -p ./ingredient -o signed_image.jpg +``` + +### Forced overwrite + +The tool will return an error if the output file already exists. Use the `--force` / `-f` option to force overwriting the output file. For example: + +```shell +c2patool sample/image.jpg -m sample/test.json -f -o signed_image.jpg +``` + +## Generating an external manifest + +Use the `--sidecar` / `-s` option to put the manifest in an external sidecar file in the same location as the output file. The manifest will have the same output filename but with a `.c2pa` extension. The tool will copy the output file but the original will be untouched. + +```shell +c2patool sample/image.jpg -s -m sample/test.json -o signed_image.jpg +``` +## Generating a remote manifest + +Use the `--remote` / `-r` option to place an HTTP reference to the manifest in the output file. The manifest is returned as an external sidecar file in the same location as the output file with the same filename but with a `.c2pa` extension. Place the manifest at the location specified by the `-r` option. When using remote manifests the remote URL should be publicly accessible to be most useful to users. When verifying an asset, remote manifests are automatically fetched. + +```shell +c2patool sample/image.jpg -r http://my_server/myasset.c2pa -m sample/test.json -o signed_image.jpg +``` + +In the example above, the tool will embed the URL `http://my_server/myasset.c2pa` in `signed_image.jpg` then fetch the manifest from that URL and save it to `signed_image.c2pa`. + +If you use both the `-s` and `-r` options, the tool embeds a manifest in the output file and also adds the remote reference. + +## Signing claim bytes with your own signer + +When generating a manifest, if the private key is not accessible on the system on which you are running the tool, use the `--signer-path` argument to specify the path to an executable that performs signing. +This executable receives the claim bytes (the bytes to be signed) from standard input (`stdin`) and outputs the signature bytes to standard output (`stdout`). + + For example, the following command signs the asset's claim bytes by using an executable named `custom-signer`: + +```shell +c2patool sample/image.jpg \ + --manifest sample/test.json \ + --output sample/signed-image.jpg \ + --signer-path ./custom-signer \ + --reserve-size 20248 \ + -f +``` + +For information on calculating the value of the `--reserve-size` argument, see `c2patool --help`. + +## Providing a manifest definition on the command line + +To provide the manifest definition in a command line argument instead of a file, use the `--config` / `-c` option. + +For example, the following command adds a custom assertion called "org.contentauth.test". + +```shell +c2patool sample/image.jpg \ + -c '{"assertions": \ + [{"label": "org.contentauth.test", \ + "data": {"my_key": "whatever I want"}}]}' +``` + +## Speeding up signing + +By default, `c2patool` validates the signature immediately after signing a manifest. To disable this and speed up the validation process, use the `--no_signing_verify` option. + +## Configuring trust support + +Enable trust support by using the `trust` subcommand, as follows: + +``` +c2patool [path] trust [OPTIONS] +``` + +When the `trust` subcommand is supplied, should c2patool encounter a problem with validating any of the claims in the asset, its JSON output will contain a `validation_status` field whose value is an array of objects, each describing a validation problem. + +### Additional options + +Several additional CLI options are available with the `trust` sub-command to specify the location of files containing the trust anchors list or known certificate list, as described in the following table. You can also use environment variables to specify these values. + +
+ +| Option | Environment variable | Description | +| ------ | -------------------- | ----------- | +| `--trust_anchors` | `C2PATOOL_TRUST_ANCHORS` | URL or relative path to a file containing a list of trust anchors (in PEM format) used to validate the manifest certificate chain. To be valid, the manifest certificate chain must lead to a certificate on the trust list. All certificates in the trust anchor list must have the [Basic Constraints extension](https://docs.digicert.com/en/iot-trust-manager/certificate-templates/create-json-formatted-certificate-templates/extensions/basic-constraints.html) and the CA attribute of this extension must be `True`. | +| `--allowed_list` | `C2PATOOL_ALLOWED_LIST` | URL or relative path to a file containing a list of end-entity certificates (in PEM format) to trust. These certificates are used to sign the manifest. Supersedes the `trust_anchors` setting. The list must NOT contain certificates with the [Basic Constraints extension](https://docs.digicert.com/en/iot-trust-manager/certificate-templates/create-json-formatted-certificate-templates/extensions/basic-constraints.html) with the CA attribute `True`. | +| `--trust_config` | `C2PATOOL_TRUST_CONFIG` | URL or relative path to a file containing the allowed set of custom certificate extended key usages (EKUs). Each entry in the list is an object identifiers in [OID dot notation](http://www.oid-info.com/#oid) format. | + +
+ +For example: + +```shell +c2patool sample/C.jpg trust \ + --allowed_list sample/allowed_list.pem \ + --trust_config sample/store.cfg +``` + +Another example with URL argument values: + +```shell +c2patool sample/C.jpg trust \ + --trust_anchors https://server.com/anchors.pem \ + --trust_config https://server.com/store.cfg +``` + +### Using the Verify known certificate list + +**IMPORTANT:** The C2PA intends to publish an official trust list. Until that time, the [C2PA Verify tool uses a temporary known certificate list](https://opensource.contentauthenticity.org/docs/verify-known-cert-list). These lists are subject to change, and will be deprecated when C2PA publishes its trust list. + +To configure C2PA tool to use the Verify temporary known certificate list, set the following environment variables on your system: + +```shell +export C2PATOOL_TRUST_ANCHORS='https://contentcredentials.org/trust/anchors.pem' +export C2PATOOL_ALLOWED_LIST='https://contentcredentials.org/trust/allowed.sha256.txt' +export C2PATOOL_TRUST_CONFIG='https://contentcredentials.org/trust/store.cfg' +``` + +**Note:** When these environment variables are set, C2PA Tool will make several HTTP requests each time it runs. Since these lists may change without notice (and the allowed list may change quite often), check these lists frequently to stay in sync with the Verify site. However, when performing bulk operations, you may want to cache these files locally to avoid a large number of network calls that might affect performance. + +You can then run: + +```shell +c2patool sample/C.jpg trust +``` + +You can also specify these values as CLI arguments instead: + +```shell +c2patool sample/C.jpg trust \ + --trust_anchors='https://contentcredentials.org/trust/anchors.pem' \ + --allowed_list='https://contentcredentials.org/trust/allowed.sha256.txt' \ + --trust_config='https://contentcredentials.org/trust/store.cfg' +``` + +**Note:** This sample image should show a `signingCredential.untrusted` validation status since the test signing certificate used to sign them is not contained on the trust lists above. + +## Adding a manifest to fragmented BMFF content + +The ISO base media file format (BMFF) is a container file format that defines a structure for files that contain time-based multimedia data such as video and audio. + +Add a manifest to a fragmented BMFF file by using the `fragment` subcommand, as follows: + +``` +c2patool fragment [--fragments_glob] +``` + +Where `` is a [glob pattern](https://en.wikipedia.org/wiki/Glob_(programming)). + +For example, to add manifest to a video file: + +``` +c2patool -m test2.json -o /1080p_out \ + /Downloads/1080p/avc1/init.mp4 \ + fragment --fragments_glob "seg-*[0-9].m4s" +``` + +Or to verify a manifest and fragments: +``` +c2patool /Downloads/1080p_out/avc1/init.mp4 \ + fragment --fragments_glob "seg-*[0-9].m4s" +``` + +### Additional option + +The `--fragments_glob` option is only available with the `fragment` subcommand and specifies the glob pattern to find the fragments of the asset. The path is automatically set to be the same as the "init" segment, so the pattern must match only segment file names, not full paths. \ No newline at end of file diff --git a/cli/docs/x_509.md b/cli/docs/x_509.md new file mode 100644 index 000000000..7b6e12203 --- /dev/null +++ b/cli/docs/x_509.md @@ -0,0 +1,31 @@ +# Using an X.509 certificate + +The c2patool uses some custom properties in the manifest definition file for signing: + +- `private_key`: Path to the private key file. +- `sign_cert`: Path to the signing certificate file. +- `alg`: Algorithm to use, if not the default ES256. + +Both the private key and signing certificate must be in PEM (privacy-enhanced mail) format. The signing certificate must contain a PEM certificate chain starting with the end-entity certificate used to sign the claim ending with the intermediate certificate before the root CA certificate. + +If the manifest definition file doesn't include the `sign_cert` and `private_key` properties, c2patool uses a built-in certificate and private key. An example certifcate and private key file are also provided in the [c2patool repo sample folder](https://github.com/contentauth/c2pa-rs/tree/main/cli/sample). + +If you are using a signing algorithm other than the default `es256`, specify it in the manifest definition field `alg` with one of the following values: + +- `ps256` +- `ps384` +- `ps512` +- `es256` +- `es384` +- `es512` +- `ed25519` + +The specified algorithm must be compatible with the values of private key and signing certificate. For more information, see [Signing manfiests](https://opensource.contentauthenticity.org/docs/signing-manifests). + +Instead of specifying the values in manifest definition file properties, you can put the values of the key and cert chain in two environment variables: `C2PA_PRIVATE_KEY` for the private key and `C2PA_SIGN_CERT` for the public certificates. For example, to sign with ES256 signatures using the content of a private key file and certificate file: + +```shell +set C2PA_PRIVATE_KEY=$(cat my_es256_private_key) +set C2PA_SIGN_CERT=$(cat my_es256_certs) +``` + diff --git a/cli/sample/C.jpg b/cli/sample/C.jpg new file mode 100644 index 000000000..03934522a Binary files /dev/null and b/cli/sample/C.jpg differ diff --git a/cli/sample/allowed_list.pem b/cli/sample/allowed_list.pem new file mode 100644 index 000000000..6183f7ed7 --- /dev/null +++ b/cli/sample/allowed_list.pem @@ -0,0 +1,92 @@ +-----BEGIN CERTIFICATE----- +MIIChzCCAi6gAwIBAgIUcCTmJHYF8dZfG0d1UdT6/LXtkeYwCgYIKoZIzj0EAwIw +gYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJl +MScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3QgQ0ExGTAXBgNV +BAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAe +Fw0yMjA2MTAxODQ2NDBaFw0zMDA4MjYxODQ2NDBaMIGAMQswCQYDVQQGEwJVUzEL +MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEfMB0GA1UECgwWQzJQQSBU +ZXN0IFNpZ25pbmcgQ2VydDEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05MWTEUMBIG +A1UEAwwLQzJQQSBTaWduZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQPaL6R +kAkYkKU4+IryBSYxJM3h77sFiMrbvbI8fG7w2Bbl9otNG/cch3DAw5rGAPV7NWky +l3QGuV/wt0MrAPDoo3gwdjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG +AQUFBwMEMA4GA1UdDwEB/wQEAwIGwDAdBgNVHQ4EFgQUFznP0y83joiNOCedQkxT +tAMyNcowHwYDVR0jBBgwFoAUDnyNcma/osnlAJTvtW6A4rYOL2swCgYIKoZIzj0E +AwIDRwAwRAIgOY/2szXjslg/MyJFZ2y7OH8giPYTsvS7UPRP9GI9NgICIDQPMKrE +LQUJEtipZ0TqvI/4mieoyRCeIiQtyuS0LACz +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +wvq03sA= +-----END CERTIFICATE----- diff --git a/cli/sample/es256_certs.pem b/cli/sample/es256_certs.pem new file mode 100644 index 000000000..f24c51cd2 --- /dev/null +++ b/cli/sample/es256_certs.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIChzCCAi6gAwIBAgIUcCTmJHYF8dZfG0d1UdT6/LXtkeYwCgYIKoZIzj0EAwIw +gYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJl +MScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3QgQ0ExGTAXBgNV +BAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAe +Fw0yMjA2MTAxODQ2NDBaFw0zMDA4MjYxODQ2NDBaMIGAMQswCQYDVQQGEwJVUzEL +MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEfMB0GA1UECgwWQzJQQSBU +ZXN0IFNpZ25pbmcgQ2VydDEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05MWTEUMBIG +A1UEAwwLQzJQQSBTaWduZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQPaL6R +kAkYkKU4+IryBSYxJM3h77sFiMrbvbI8fG7w2Bbl9otNG/cch3DAw5rGAPV7NWky +l3QGuV/wt0MrAPDoo3gwdjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG +AQUFBwMEMA4GA1UdDwEB/wQEAwIGwDAdBgNVHQ4EFgQUFznP0y83joiNOCedQkxT +tAMyNcowHwYDVR0jBBgwFoAUDnyNcma/osnlAJTvtW6A4rYOL2swCgYIKoZIzj0E +AwIDRwAwRAIgOY/2szXjslg/MyJFZ2y7OH8giPYTsvS7UPRP9GI9NgICIDQPMKrE +LQUJEtipZ0TqvI/4mieoyRCeIiQtyuS0LACz +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICajCCAg+gAwIBAgIUfXDXHH+6GtA2QEBX2IvJ2YnGMnUwCgYIKoZIzj0EAwIw +dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx +GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO +R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTIyMDYxMDE4NDY0MFoXDTMwMDgy +NzE4NDY0MFowgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJ +U29tZXdoZXJlMScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3Qg +Q0ExGTAXBgNVBAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVk +aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHllI4O7a0EkpTYAWfPM +D6Rnfk9iqhEmCQKMOR6J47Rvh2GGjUw4CS+aLT89ySukPTnzGsMQ4jK9d3V4Aq4Q +LsOjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBQOfI1yZr+iyeUAlO+1boDitg4vazAfBgNVHSMEGDAWgBRembiG4Xgb2VcVWnUA +UrYpDsuojDAKBggqhkjOPQQDAgNJADBGAiEAtdZ3+05CzFo90fWeZ4woeJcNQC4B +84Ill3YeZVvR8ZECIQDVRdha1xEDKuNTAManY0zthSosfXcvLnZui1A/y/DYeg== +-----END CERTIFICATE----- + diff --git a/cli/sample/es256_private.key b/cli/sample/es256_private.key new file mode 100644 index 000000000..5e59fcc5e --- /dev/null +++ b/cli/sample/es256_private.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgfNJBsaRLSeHizv0m +GL+gcn78QmtfLSm+n+qG9veC2W2hRANCAAQPaL6RkAkYkKU4+IryBSYxJM3h77sF +iMrbvbI8fG7w2Bbl9otNG/cch3DAw5rGAPV7NWkyl3QGuV/wt0MrAPDo +-----END PRIVATE KEY----- diff --git a/cli/sample/image.jpg b/cli/sample/image.jpg new file mode 100644 index 000000000..be277a89a Binary files /dev/null and b/cli/sample/image.jpg differ diff --git a/cli/sample/ps256.pem b/cli/sample/ps256.pem new file mode 100644 index 000000000..ed4c7318d --- /dev/null +++ b/cli/sample/ps256.pem @@ -0,0 +1,53 @@ +-----BEGIN PRIVATE KEY----- +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 +G/dZR0Qj3j0T5F9GX719XpmzYbVFKIKPTsKF +-----END PRIVATE KEY----- diff --git a/cli/sample/ps256.pub b/cli/sample/ps256.pub new file mode 100644 index 000000000..9e6aa6ce4 --- /dev/null +++ b/cli/sample/ps256.pub @@ -0,0 +1,113 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +wvq03sA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/cli/sample/store.cfg b/cli/sample/store.cfg new file mode 100644 index 000000000..93415a535 --- /dev/null +++ b/cli/sample/store.cfg @@ -0,0 +1,9 @@ + +//id-kp-emailProtection +1.3.6.1.5.5.7.3.4 +//id-kp-documentSigning +1.3.6.1.5.5.7.3.36 +//id-kp-timeStamping +1.3.6.1.5.5.7.3.8 +//id-kp-OCSPSigning +1.3.6.1.5.5.7.3.9 \ No newline at end of file diff --git a/cli/sample/test.json b/cli/sample/test.json new file mode 100644 index 000000000..879b56761 --- /dev/null +++ b/cli/sample/test.json @@ -0,0 +1,48 @@ +{ + "alg": "ps256", + "private_key": "ps256.pem", + "sign_cert": "ps256.pub", + "ta_url": "http://timestamp.digicert.com", + "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" + } + ] + } + }, + { + "label": "c2pa.actions", + "data": { + "actions": [ + { + "action": "c2pa.opened" + } + ], + "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/cli/sample/trust_anchors.pem b/cli/sample/trust_anchors.pem new file mode 100644 index 000000000..7ad0efb6b --- /dev/null +++ b/cli/sample/trust_anchors.pem @@ -0,0 +1,54 @@ +-----BEGIN CERTIFICATE----- +MIICUzCCAfmgAwIBAgIUdmkq4byvgk2FSnddHqB2yjoD68gwCgYIKoZIzj0EAwIw +dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx +GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO +R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTIyMDYxMDE4NDY0MFoXDTMyMDYw +NzE4NDY0MFowdzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlT +b21ld2hlcmUxGjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBG +T1IgVEVTVElOR19PTkxZMRAwDgYDVQQDDAdSb290IENBMFkwEwYHKoZIzj0CAQYI +KoZIzj0DAQcDQgAEre/KpcWwGEHt+mD4xso3xotRnRx2IEsMoYwVIKI7iEJrDEye +PcvJuBywA0qiMw2yvAvGOzW/fqUTu1jABrFIk6NjMGEwHQYDVR0OBBYEFF6ZuIbh +eBvZVxVadQBStikOy6iMMB8GA1UdIwQYMBaAFF6ZuIbheBvZVxVadQBStikOy6iM +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0gA +MEUCIHBC1xLwkCWSGhVXFlSnQBx9cGZivXzCbt8BuwRqPSUoAiEAteZQDk685yh9 +jgOTkp4H8oAmM1As+qlkRK2b+CHAQ3k= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + + diff --git a/cli/schemas/ingredient.json b/cli/schemas/ingredient.json new file mode 100644 index 000000000..8ad144dec --- /dev/null +++ b/cli/schemas/ingredient.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://ns.adobe.com/c2patool/ingredient-definition/v1", + "type": "object", + "description": "Definition format for an ingredient created with c2patool", + "examples": [ + { + "title": "C.jpg", + "format": "image/jpeg", + "instance_id": "xmp:iid:01712cdc-26b8-4902-ad53-1cdbd5370b1b", + "relationship": "componentOf", + "thumbnail": { + "format": "image/jpeg", + "identifier": "thumb.jpg" + }, + "active_manifest": "manifest.c2pa" + } + ], + "required": [ + "title" + ], + "properties": { + "title": { + "type": "string", + "description": "A human-readable string to be displayed as the title for this Ingredient (generally the file name of the asset)." + }, + "format": { + "type": "string", + "description": "The MIME content type of the associated ingredient" + }, + "instance_id": { + "type": "string", + "description": "A unique identifier for the ingredient instance. Often from `xmpMM:InstanceID` in XMP metadata" + }, + "document_id": { + "type": "string", + "description": "Optionally a copy from `xmpMM:DocumentID` in XMP metadata" + }, + "relationship": { + "type": "string", + "description": "Either `componentOf`(default) or `parentOf`" + }, + "provenance": { + "type": "string", + "description": "URI to the associated C2PA manifest. My echo `dcterms:provenance` in XMP metadata." + }, + "hash": { + "type": "string", + "description": "A hash value of the asset at import, used to deduplicate ingredients" + }, + "thumbnail": { + "type": "object", + "format": "ResourceReference", + "description": "Identifies a thumbnail file with `format` holding MIME type, and `identifier` with a path to the file (relative to Ingredient definition)" + }, + "active_manifest": { + "type": "string", + "description": "Manifest label of for the most recently added manifest in the manifest store" + }, + "validation_status": { + "type": "object", + "format": "ValidationStatus", + "description": "Validation status record for any issues related to this ingredient or its validation" + }, + "manifest_data": { + "type": "object", + "format": "ResourceRef", + "description": "Identifies a c2pa file the extracted manifest store from an asset with `format` holding MIME type, and `identifier` with a path to the .c2pa file (relative to Ingredient definition)" + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/cli/schemas/manifest-definition.json b/cli/schemas/manifest-definition.json new file mode 100644 index 000000000..57eee661c --- /dev/null +++ b/cli/schemas/manifest-definition.json @@ -0,0 +1,103 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "http://ns.adobe.com/c2patool/claim-definition/v1", + "type": "object", + "description": "Definition format for claim created with c2patool", + "examples": [ + { + "alg": "es256", + "private_key": "es256_private.key", + "sign_cert": "es256_certs.pem", + "ta_url": "http://timestamp.digicert.com", + "vendor": "myvendor", + "claim_generator": "MyApp/0.1", + "thumbnail": { + "identifier": "thumb.jpg", + "format": "image/jpeg" + }, + "ingredients": [ + { + "title": "A.jpg", + "instanceId": "12345", + "thumbnail": { + "identifier": "A_thumb.jpg", + "format": "image/jpeg" + }, + "relationship": "parentOf" + } + ], + "ingredient_paths": [ + "C.jpg" + ], + "assertions": [ + { + "label": "my.assertion", + "data": { + "any_tag": "whatever I want" + } + } + ] + } + ], + "required": [ + "assertions" + ], + "properties": { + "vendor": { + "type": "string", + "description": "Typically an Internet domain name (without the TLD) for the vendor (i.e. `adobe`, `nytimes`). If provided this will be used as a prefix on generated manifest labels." + }, + "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 title for this Manifest (defaults to the name of the file this manifest was embedded in)." + }, + "credentials": { + "type": "object", + "description": "An array of W3C verifiable credentials objects defined in the c2pa assertion specification. Section 7." + }, + "thumbnail": { + "type": "object", + "format": "JSON resource definition", + "description": "An object with an identifier field with a file path, and a format with the mime type of that file." + }, + "ingredients": { + "type": "array", + "format": "Array of JSON ingredients, such as those produced with the --ingredient option", + "description": "Ingredients that were used to modify the asset referenced by this Manifest (if any)." + }, + "ingredient_paths": { + "type": "array", + "format": "Array of local file system paths", + "description": "File paths to assets that were used to modify the asset referenced by this Manifest (if any). This may be a JSON Ingredient definition file." + }, + "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]. Defaults to es256." + }, + "ta_url": { + "type": "string", + "format": "http URL", + "description": "A URL to an RFC3161 compliant Time Stamp Authority. If missing there will no secure timestamp." + }, + "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." + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/cli/src/callback_signer.rs b/cli/src/callback_signer.rs new file mode 100644 index 000000000..b07ef6290 --- /dev/null +++ b/cli/src/callback_signer.rs @@ -0,0 +1,405 @@ +// 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::{ + io::Write, + path::PathBuf, + process::{Command, Stdio}, +}; + +use anyhow::{bail, Context}; +use c2pa::{Error, Signer, SigningAlg}; +use c2pa_crypto::{ + raw_signature::{RawSigner, RawSignerError}, + time_stamp::TimeStampProvider, +}; + +use crate::signer::SignConfig; + +/// A struct that implements [SignCallback]. This struct will call out to the client provided +/// external signer to get the signed bytes for the asset. +pub(crate) struct ExternalProcessRunner { + config: CallbackSignerConfig, + signer_path: PathBuf, +} + +impl ExternalProcessRunner { + pub fn new(config: CallbackSignerConfig, signer_path: PathBuf) -> Self { + Self { + config, + signer_path, + } + } +} + +impl SignCallback for ExternalProcessRunner { + /// Runs the client-provided [Command], passing to it, via stdin, the bytes to be signed. We + /// also pass the `reserve-size`, `sign-cert`, and `alg` as CLI arguments to the [Command]. + fn sign(&self, bytes: &[u8]) -> anyhow::Result> { + let sign_cert = self + .config + .sign_cert_path + .as_os_str() + .to_str() + .context("Unable to read sign_certs. Is the sign_cert path valid?")?; + + // Spawn external process provided by the `c2patool` client. + let mut child = Command::new(&self.signer_path) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .args(["--reserve-size", &self.config.reserve_size.to_string()]) + .args(["--alg", &format!("{}", &self.config.alg)]) + .args(["--sign-cert", sign_cert]) + .spawn() + .context(format!("Failed to run command at {:?}", self.signer_path))?; + + // Write claim bytes to spawned processes' `stdin`. + child + .stdin + .take() + .context("Failed to access `stdin` of external process")? + .write_all(bytes) + .context("Failed to write data to the provided external process")?; + + let output = child + .wait_with_output() + .context(format!("Failed to read stdout from {:?}", self.signer_path))?; + + if !output.status.success() { + bail!(format!( + "User supplied signer process failed. It's stderr output was: \n{}", + String::from_utf8(output.stderr).unwrap_or_default() + )); + } + + let bytes = output.stdout; + if bytes.is_empty() { + bail!("User supplied process succeeded, but the external process did not write signature bytes to stdout"); + } + + Ok(bytes) + } +} + +/// A config containing the required values for signing an asset with an external command. +#[derive(Clone, Debug)] +pub(crate) struct CallbackSignerConfig { + /// Signing algorithm to use - must match the associated certs + /// + /// Must be one of [ ps256 | ps384 | ps51024 | es256 | es384 | es51024 | ed25519 ] + pub alg: SigningAlg, + /// A path to a file containing the signing cert required for signing + pub sign_cert_path: PathBuf, + /// Size of the claim bytes. + pub reserve_size: usize, + pub tsa_url: Option, +} + +impl CallbackSignerConfig { + /// Constructs a new [CallbackSignerConfig] using a manifest sign config, the name of an + /// external process, and the reserve_size. + pub fn new(sign_config: &SignConfig, reserve_size: usize) -> anyhow::Result { + let alg = sign_config + .alg + .clone() + .and_then(|alg| alg.parse::().ok()) + .context("Invalid signing algorithm provided")?; + + let sign_cert_path = sign_config + .sign_cert + .clone() + .context("Unable to load the provided sign_cert_path")?; + + Ok(CallbackSignerConfig { + alg, + sign_cert_path, + reserve_size, + tsa_url: sign_config.ta_url.clone(), + }) + } +} + +#[cfg_attr(test, mockall::automock)] +pub(crate) trait SignCallback { + /// Method which will be called with the `data` to be signed. Implementors + /// should return the signed bytes as an [anyhow::Result]. + fn sign(&self, data: &[u8]) -> anyhow::Result>; +} + +/// A [Signer] implementation that allows clients to provide their own function +/// to sign the manifest bytes. +pub(crate) struct CallbackSigner<'a> { + callback: Box, + config: CallbackSignerConfig, +} + +impl<'a> CallbackSigner<'a> { + pub fn new(callback: Box, config: CallbackSignerConfig) -> Self { + Self { callback, config } + } +} + +impl Signer for CallbackSigner<'_> { + fn sign(&self, data: &[u8]) -> c2pa::Result> { + self.callback.sign(data).map_err(|e| { + eprintln!("Unable to embed signature into asset. {}", e); + Error::EmbeddingError + }) + } + + fn alg(&self) -> SigningAlg { + self.config.alg + } + + fn certs(&self) -> c2pa::Result>> { + let cert_contents = std::fs::read(&self.config.sign_cert_path) + .map_err(|_| Error::FileNotFound(format!("{:?}", self.config.sign_cert_path)))?; + + let mut pems = pem::parse_many(cert_contents).map_err(|_| Error::CoseInvalidCert)?; + // [pem::parse_many] returns an empty vector if you supply invalid contents, like json, for example. + // Check here if the pems vector is empty. + if pems.is_empty() { + return Err(Error::CoseInvalidCert); + } + + let sign_cert = pems + .drain(..) + .map(|p| p.into_contents()) + .collect::>>(); + + Ok(sign_cert) + } + + fn reserve_size(&self) -> usize { + self.config.reserve_size + } + + fn time_authority_url(&self) -> Option { + self.config.tsa_url.clone() + } + + fn raw_signer(&self) -> Box<&dyn RawSigner> { + Box::new(self) + } +} + +impl RawSigner for CallbackSigner<'_> { + fn sign(&self, data: &[u8]) -> Result, RawSignerError> { + self.callback.sign(data).map_err(|e| { + eprintln!("Unable to embed signature into asset. {}", e); + RawSignerError::InternalError(e.to_string()) + }) + } + + fn alg(&self) -> SigningAlg { + self.config.alg + } + + fn cert_chain(&self) -> Result>, RawSignerError> { + let cert_contents = std::fs::read(&self.config.sign_cert_path)?; + + let mut pems = pem::parse_many(cert_contents).map_err(|_| Error::CoseInvalidCert)?; + // [pem::parse_many] returns an empty vector if you supply invalid contents, like json, for example. + // Check here if the pems vector is empty. + if pems.is_empty() { + return Err(RawSignerError::InvalidSigningCredentials( + "no certificates provided".to_string(), + )); + } + + let sign_cert = pems + .drain(..) + .map(|p| p.into_contents()) + .collect::>>(); + + Ok(sign_cert) + } + + fn reserve_size(&self) -> usize { + self.config.reserve_size + } +} + +impl TimeStampProvider for CallbackSigner<'_> { + fn time_stamp_service_url(&self) -> Option { + self.config.tsa_url.clone() + } +} + +#[cfg(test)] +mod test { + use anyhow::anyhow; + + use super::*; + + #[test] + fn test_signing_succeeds_returns_bytes() { + let mut sign_cert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + sign_cert_path.push("sample/es256_certs.pem"); + + let sign_config = SignConfig { + alg: Some(SigningAlg::Es256.to_string()), + sign_cert: Some(sign_cert_path), + ..Default::default() + }; + + let result = vec![1, 2, 3]; + let expected = result.clone(); + + let mut mock_callback_signer = MockSignCallback::default(); + mock_callback_signer + .expect_sign() + .returning(move |_| Ok(result.clone())); + + let config = CallbackSignerConfig::new(&sign_config, 1024).unwrap(); + let callback = Box::new(mock_callback_signer); + let signer = CallbackSigner::new(callback, config); + + assert_eq!(Signer::sign(&signer, &[]).unwrap(), expected); + } + + #[test] + fn test_signing_succeeds_returns_error_embedding() { + let mut sign_cert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + sign_cert_path.push("sample/es256_certs.pem"); + + let sign_config = SignConfig { + alg: Some(SigningAlg::Es256.to_string()), + sign_cert: Some(sign_cert_path), + ..Default::default() + }; + + let mut mock_callback_signer = MockSignCallback::default(); + mock_callback_signer + .expect_sign() + .returning(|_| Err(anyhow!(""))); + + let config = CallbackSignerConfig::new(&sign_config, 1024).unwrap(); + let callback = Box::new(mock_callback_signer); + let signer = CallbackSigner::new(callback, config); + + assert!(matches!( + Signer::sign(&signer, &[]), + Err(Error::EmbeddingError) + )); + } + + #[test] + fn test_sign_config_to_external_sign_config_fails() { + let sign_config = SignConfig::default(); + assert!(CallbackSignerConfig::new(&sign_config, 1024).is_err()); + } + + #[test] + fn test_sign_config_to_external_sign_config_fails_with_invalid_signing_alg() { + let sign_config = SignConfig { + alg: Some("invalid_signing_alg".to_owned()), + ..Default::default() + }; + + let result = CallbackSignerConfig::new(&sign_config, 1024); + let error = result.err().unwrap(); + assert_eq!(format!("{error}"), "Invalid signing algorithm provided") + } + + #[test] + fn test_sign_config_to_external_sign_config_fails_with_missing_sign_certs() { + let sign_config = SignConfig { + alg: Some(SigningAlg::Es256.to_string()), + sign_cert: None, + ..Default::default() + }; + + let result = CallbackSignerConfig::new(&sign_config, 1024); + let error = result.err().unwrap(); + assert_eq!( + format!("{error}"), + "Unable to load the provided sign_cert_path" + ) + } + + #[test] + fn test_try_from_succeeds_for_valid_sign_config() { + let mut sign_cert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + sign_cert_path.push("sample/es256_certs.pem"); + + let expected_alg = SigningAlg::Es256; + let sign_config = SignConfig { + alg: Some(expected_alg.to_string()), + sign_cert: Some(sign_cert_path), + ..Default::default() + }; + + let expected_reserve_size = 10248; + let esc = CallbackSignerConfig::new(&sign_config, expected_reserve_size).unwrap(); + let callback = Box::::default(); + let signer = CallbackSigner::new(callback, esc); + + assert_eq!(Signer::alg(&signer), expected_alg); + assert_eq!(Signer::reserve_size(&signer), expected_reserve_size); + } + + #[test] + fn test_callback_signer_error_file_not_found() { + let mut sign_cert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + sign_cert_path.push("sample/NOT-HERE"); + + let sign_config = SignConfig { + alg: Some(SigningAlg::Es256.to_string()), + sign_cert: Some(sign_cert_path), + ..Default::default() + }; + + let config = CallbackSignerConfig::new(&sign_config, 10248).unwrap(); + let callback = Box::::default(); + let signer = CallbackSigner::new(callback, config); + + assert!(matches!(signer.certs(), Err(Error::FileNotFound(_)))); + } + + #[test] + fn test_callback_signer_error_invalid_cert() { + let mut sign_cert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + sign_cert_path.push("sample/test.json"); + + let sign_config = SignConfig { + alg: Some(SigningAlg::Es256.to_string()), + sign_cert: Some(sign_cert_path), + ..Default::default() + }; + + let config = CallbackSignerConfig::new(&sign_config, 1024).unwrap(); + let callback = Box::::default(); + let signer = CallbackSigner::new(callback, config); + + assert!(matches!(signer.certs(), Err(Error::CoseInvalidCert))); + } + + #[test] + fn test_callback_signer_valid_sign_certs() { + let mut sign_cert_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + sign_cert_path.push("sample/es256_certs.pem"); + + let sign_config = SignConfig { + alg: Some(SigningAlg::Es256.to_string()), + sign_cert: Some(sign_cert_path), + ..Default::default() + }; + + let config = CallbackSignerConfig::new(&sign_config, 1024).unwrap(); + let callback = Box::::default(); + let signer = CallbackSigner::new(callback, config); + + assert_eq!(signer.certs().unwrap().len(), 2); + } +} diff --git a/cli/src/info.rs b/cli/src/info.rs new file mode 100644 index 000000000..c0f85f65b --- /dev/null +++ b/cli/src/info.rs @@ -0,0 +1,91 @@ +// 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 std::{io::Cursor, path::Path}; + +use anyhow::Result; +use c2pa::{IngredientOptions, Reader}; + +/// Display additional C2PA information about the asset (not JSON formatted). +pub fn info(path: &Path) -> Result<()> { + struct Options {} + impl IngredientOptions for Options { + fn thumbnail(&self, _path: &Path) -> Option<(String, Vec)> { + None + } + } + let ingredient = c2pa::Ingredient::from_file_with_options(path, &Options {})?; + println!("Information for {}", ingredient.title().unwrap_or_default()); + let mut is_cloud_manifest = false; + //println!("instanceID = {}", ingredient.instance_id()); + if let Some(provenance) = ingredient.provenance() { + is_cloud_manifest = !provenance.starts_with("self#jumbf="); + if is_cloud_manifest { + println!("Cloud URL = {provenance}"); + } else { + println!("Provenance URI = {provenance}"); + } + } + + let file_size = std::fs::metadata(path).unwrap().len(); + if let Some(manifest_data) = ingredient.manifest_data() { + if is_cloud_manifest { + println!( + "Remote manifest store size = {} (file size = {})", + manifest_data.len(), + file_size + ); + } else { + println!( + "Manifest store size = {} ({:.2}% of file size {})", + manifest_data.len(), + (manifest_data.len() as f64 / file_size as f64) * 100f64, + file_size + ); + } + if let Some(validation_status) = ingredient.validation_status() { + println!("Validation issues:"); + for status in validation_status { + println!(" {}", status.code()); + } + } else { + println!("Validated"); + } + let reader = Reader::from_stream("c2pa", Cursor::new(manifest_data.into_owned()))?; + + let manifests: Vec<_> = reader.iter_manifests().collect(); + match manifests.len() { + 0 => println!("No manifests"), + 1 => println!("One manifest"), + n => println!("{n} manifests"), + } + } else if is_cloud_manifest { + println!("Unable to fetch cloud manifest. (file size = {file_size})"); + } else { + println!("No C2PA Manifests. (file size = {file_size})"); + } + Ok(()) +} + +#[cfg(test)] +pub mod tests { + #![allow(clippy::expect_used)] + + use super::*; + + #[test] + fn test_manifest_config() { + const SOURCE_PATH: &str = "tests/fixtures/C.jpg"; + + info(&std::path::PathBuf::from(SOURCE_PATH)).expect("info"); + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 000000000..9f973b533 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,734 @@ +// 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 an asset must be provided. If only the path +/// is given, this will generate a summary report of any claims +/// in that file. If a manifest definition JSON file is specified, +/// the claim will be added to any existing claims. +use std::{ + fs::{create_dir_all, remove_dir_all, File}, + io::Write, + path::{Path, PathBuf}, + str::FromStr, +}; + +use anyhow::{anyhow, bail, Context, Result}; +use c2pa::{Builder, ClaimGeneratorInfo, Error, Ingredient, ManifestDefinition, Reader, Signer}; +use clap::{Parser, Subcommand}; +use log::debug; +use serde::Deserialize; +use signer::SignConfig; +use url::Url; + +use crate::{ + callback_signer::{CallbackSigner, CallbackSignerConfig, ExternalProcessRunner}, + info::info, +}; + +mod info; +mod tree; + +mod callback_signer; +mod signer; + +/// Tool for displaying and creating C2PA manifests. +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None, arg_required_else_help(true))] +struct CliArgs { + /// Path to manifest definition JSON file. + #[clap(short, long, requires = "output")] + manifest: Option, + + /// Path to output file or folder. + #[clap(short, long)] + output: Option, + + /// Path to a parent file. + #[clap(short, long)] + parent: Option, + + /// Manifest definition passed as a JSON string. + #[clap(short, long, conflicts_with = "manifest")] + config: Option, + + /// Display detailed C2PA-formatted manifest data. + #[clap(short, long)] + detailed: bool, + + /// Force overwrite of output if it already exists. + #[clap(short, long)] + force: bool, + + /// The path to an asset to examine or embed a manifest into. + path: PathBuf, + + /// Embed remote URL manifest reference. + #[clap(short, long)] + remote: Option, + + /// Generate a sidecar (.c2pa) manifest + #[clap(short, long)] + sidecar: bool, + + /// Write ingredient report and assets to a folder. + #[clap(short, long)] + ingredient: bool, + + /// Create a tree diagram of the manifest store. + #[clap(long)] + tree: bool, + + /// Extract certificate chain. + #[clap(long = "certs")] + cert_chain: bool, + + /// Do not perform validation of signature after signing + #[clap(long = "no_signing_verify")] + no_signing_verify: bool, + + #[command(subcommand)] + command: Option, + + /// Show manifest size, XMP url and other stats. + #[clap(long)] + info: bool, + + /// Path to an executable that will sign the claim bytes. + #[clap(long)] + signer_path: Option, + + /// To be used with the [callback_signer] argument. This value should at least: size of CoseSign1 CBOR + + /// the size of certificate chain provided in the manifest definition's `sign_cert` field + the size of the + /// signature of the Time Stamp Authority response. A typical size of CoseSign1 CBOR is in the 1-2K range. If + /// the reserve size is too small an error will be returned during signing. + /// For example: + /// + /// The reserve-size can be calculated like this if you aren't including a `tsa_url` key in + /// your manifest description: + /// + /// 1024 + sign_cert.len() + /// + /// Or, if you are including a `tsa_url` in your manifest definition, you will calculate the + /// reserve size like this: + /// + /// 1024 + sign_cert.len() + tsa_signature_response.len() + /// + /// Note: + /// We'll default the `reserve-size` to a value of 20_000, if no value is provided. This + /// will probably leave extra `0`s of unused space. Please specify a reserve-size if possible. + #[clap(long, default_value("20000"))] + reserve_size: usize, +} + +#[derive(Clone, Debug)] +enum TrustResource { + File(PathBuf), + Url(Url), +} + +fn parse_resource_string(s: &str) -> Result { + if let Ok(url) = s.parse::() { + Ok(TrustResource::Url(url)) + } else { + let p = PathBuf::from_str(s)?; + + Ok(TrustResource::File(p)) + } +} + +// We only construct one per invocation, not worth shrinking this. +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Subcommand)] +#[allow(clippy::large_enum_variant)] +enum Commands { + /// Sub-command to configure trust store options, "trust --help for more details" + Trust { + /// URL or path to file containing list of trust anchors in PEM format + #[arg(long = "trust_anchors", env="C2PATOOL_TRUST_ANCHORS", value_parser = parse_resource_string)] + trust_anchors: Option, + + /// URL or path to file containing specific manifest signing certificates in PEM format to implicitly trust + #[arg(long = "allowed_list", env="C2PATOOL_ALLOWED_LIST", value_parser = parse_resource_string)] + allowed_list: Option, + + /// URL or path to file containing configured EKUs in Oid dot notation + #[arg(long = "trust_config", env="C2PATOOL_TRUST_CONFIG", value_parser = parse_resource_string)] + trust_config: Option, + }, + /// Sub-command to add manifest to fragmented BMFF content + /// + /// The init path can be a glob to process entire directories of content, for example: + /// + /// c2patool -m test2.json -o /my_output_folder "/my_renditions/**/my_init.mp4" fragment --fragments_glob "myfile_abc*[0-9].m4s" + /// + /// Note: the glob patterns are quoted to prevent shell expansion. + Fragment { + /// Glob pattern to find the fragments of the asset. The path is automatically set to be the same as + /// the init segment. + /// + /// The fragments_glob pattern should only match fragment file names not the full paths (e.g. "myfile_abc*[0-9].m4s" + /// to match [myfile_abc1.m4s, myfile_abc2180.m4s, ...] ) + #[arg(long = "fragments_glob", verbatim_doc_comment)] + fragments_glob: Option, + }, +} + +#[derive(Debug, Default, Deserialize)] +// Add fields that are not part of the standard Manifest +struct ManifestDef { + #[serde(flatten)] + manifest: ManifestDefinition, + // allows adding ingredients with file paths + ingredient_paths: Option>, +} + +// convert certain errors to output messages +fn special_errs(e: c2pa::Error) -> anyhow::Error { + match e { + Error::JumbfNotFound => anyhow!("No claim found"), + Error::FileNotFound(name) => anyhow!("File not found: {}", name), + Error::UnsupportedType => anyhow!("Unsupported file type"), + Error::PrereleaseError => anyhow!("Prerelease claim found"), + _ => e.into(), + } +} + +// normalize extensions so we can compare them +fn ext_normal(path: &Path) -> String { + let ext = path + .extension() + .unwrap_or_default() + .to_str() + .unwrap_or_default() + .to_lowercase(); + match ext.as_str() { + "jpeg" => "jpg".to_string(), + "tiff" => "tif".to_string(), + _ => ext, + } +} + +// loads an ingredient, allowing for a folder or json ingredient +fn load_ingredient(path: &Path) -> Result { + // if the path is a folder, look for ingredient.json + let mut path_buf = PathBuf::from(path); + let path = if path.is_dir() { + path_buf = path_buf.join("ingredient.json"); + path_buf.as_path() + } else { + path + }; + if path.extension() == Some(std::ffi::OsStr::new("json")) { + let json = std::fs::read_to_string(path)?; + let mut ingredient: Ingredient = serde_json::from_slice(json.as_bytes())?; + if let Some(base) = path.parent() { + ingredient.resources_mut().set_base_path(base); + } + Ok(ingredient) + } else { + Ok(Ingredient::from_file(path)?) + } +} + +fn load_trust_resource(resource: &TrustResource) -> Result { + match resource { + TrustResource::File(path) => { + let data = std::fs::read_to_string(path) + .with_context(|| format!("Failed to read trust resource from path: {:?}", path))?; + + Ok(data) + } + TrustResource::Url(url) => { + let data = reqwest::blocking::get(url.to_string())? + .text() + .with_context(|| format!("Failed to read trust resource from URL: {}", url))?; + + Ok(data) + } + } +} + +fn configure_sdk(args: &CliArgs) -> Result<()> { + const TA: &str = r#"{"trust": { "trust_anchors": replacement_val } }"#; + const AL: &str = r#"{"trust": { "allowed_list": replacement_val } }"#; + const TC: &str = r#"{"trust": { "trust_config": replacement_val } }"#; + const VS: &str = r#"{"verify": { "verify_after_sign": replacement_val } }"#; + + let mut enable_trust_checks = false; + + if let Some(Commands::Trust { + trust_anchors, + allowed_list, + trust_config, + }) = &args.command + { + if let Some(trust_list) = &trust_anchors { + let data = load_trust_resource(trust_list)?; + debug!("Using trust anchors from {:?}", trust_list); + let replacement_val = serde_json::Value::String(data).to_string(); // escape string + let setting = TA.replace("replacement_val", &replacement_val); + + c2pa::settings::load_settings_from_str(&setting, "json")?; + + enable_trust_checks = true; + } + + if let Some(allowed_list) = &allowed_list { + let data = load_trust_resource(allowed_list)?; + debug!("Using allowed list from {:?}", allowed_list); + let replacement_val = serde_json::Value::String(data).to_string(); // escape string + let setting = AL.replace("replacement_val", &replacement_val); + + c2pa::settings::load_settings_from_str(&setting, "json")?; + + enable_trust_checks = true; + } + + if let Some(trust_config) = &trust_config { + let data = load_trust_resource(trust_config)?; + debug!("Using trust config from {:?}", trust_config); + let replacement_val = serde_json::Value::String(data).to_string(); // escape string + let setting = TC.replace("replacement_val", &replacement_val); + + c2pa::settings::load_settings_from_str(&setting, "json")?; + + enable_trust_checks = true; + } + } + + // if any trust setting is provided enable the trust checks + if enable_trust_checks { + c2pa::settings::load_settings_from_str(r#"{"verify": { "verify_trust": true} }"#, "json")?; + } else { + c2pa::settings::load_settings_from_str(r#"{"verify": { "verify_trust": false} }"#, "json")?; + } + + // enable or disable verification after signing + { + let replacement_val = serde_json::Value::Bool(!args.no_signing_verify).to_string(); + let setting = VS.replace("replacement_val", &replacement_val); + + c2pa::settings::load_settings_from_str(&setting, "json")?; + } + + Ok(()) +} + +fn sign_fragmented( + builder: &mut Builder, + signer: &dyn Signer, + init_pattern: &Path, + frag_pattern: &PathBuf, + output_path: &Path, +) -> Result<()> { + // search folders for init segments + let ip = init_pattern.to_str().ok_or(c2pa::Error::OtherError( + "could not parse source pattern".into(), + ))?; + let inits = glob::glob(ip).context("could not process glob pattern")?; + let mut count = 0; + for init in inits { + match init { + Ok(p) => { + let mut fragments = Vec::new(); + let init_dir = p.parent().context("init segment had no parent dir")?; + let seg_glob = init_dir.join(frag_pattern); // segment match pattern + + // grab the fragments that go with this init segment + let seg_glob_str = seg_glob.to_str().context("fragment path not valid")?; + let seg_paths = glob::glob(seg_glob_str).context("fragment glob not valid")?; + for seg in seg_paths { + match seg { + Ok(f) => fragments.push(f), + Err(_) => return Err(anyhow!("fragment path not valid")), + } + } + + println!("Adding manifest to: {:?}", p); + let new_output_path = + output_path.join(init_dir.file_name().context("invalid file name")?); + builder.sign_fragmented_files(signer, &p, &fragments, &new_output_path)?; + + count += 1; + } + Err(_) => bail!("bad path to init segment"), + } + } + if count == 0 { + println!("No files matching pattern: {}", ip); + } + Ok(()) +} + +fn verify_fragmented(init_pattern: &Path, frag_pattern: &Path) -> Result> { + let mut readers = Vec::new(); + + let ip = init_pattern + .to_str() + .context("could not parse source pattern")?; + let inits = glob::glob(ip).context("could not process glob pattern")?; + let mut count = 0; + + // search folders for init segments + for init in inits { + match init { + Ok(p) => { + let mut fragments = Vec::new(); + let init_dir = p.parent().context("init segment had no parent dir")?; + let seg_glob = init_dir.join(frag_pattern); // segment match pattern + + // grab the fragments that go with this init segment + let seg_glob_str = seg_glob.to_str().context("fragment path not valid")?; + let seg_paths = glob::glob(seg_glob_str).context("fragment glob not valid")?; + for seg in seg_paths { + match seg { + Ok(f) => fragments.push(f), + Err(_) => return Err(anyhow!("fragment path not valid")), + } + } + + println!("Verifying manifest: {:?}", p); + let reader = Reader::from_fragmented_files(p, &fragments)?; + if let Some(vs) = reader.validation_status() { + if let Some(e) = vs.iter().find(|v| !v.passed()) { + eprintln!("Error validating segments: {:?}", e); + return Ok(readers); + } + } + + readers.push(reader); + + count += 1; + } + Err(_) => bail!("bad path to init segment"), + } + } + + if count == 0 { + println!("No files matching pattern: {}", ip); + } + + Ok(readers) +} + +fn main() -> Result<()> { + let args = CliArgs::parse(); + + // 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 path = &args.path; + + if args.info { + return info(path); + } + + if args.cert_chain { + let reader = Reader::from_file(path).map_err(special_errs)?; + if let Some(manifest) = reader.active_manifest() { + if let Some(si) = manifest.signature_info() { + println!("{}", si.cert_chain()); + // todo: add ocsp validation info + return Ok(()); + } + } + bail!("No certificate chain found"); + } + + if args.tree { + println!("{}", tree::tree(path)?); + return Ok(()); + } + + let is_fragment = matches!( + &args.command, + Some(Commands::Fragment { fragments_glob: _ }) + ); + + // configure the SDK + configure_sdk(&args).context("Could not configure c2pa-rs")?; + + // Remove manifest needs to also remove XMP provenance + // if args.remove_manifest { + // match args.output { + // Some(output) => { + // if output.exists() && !args.force { + // bail!("Output already exists, use -f/force to force write"); + // } + // if path != &output { + // std::fs::copy(path, &output)?; + // } + // Manifest::remove_manifest(&output)? + // }, + // None => { + // bail!("The -o/--output argument is required for this operation"); + // } + // } + // return Ok(()); + // } + + // if we have a manifest config, process it + if args.manifest.is_some() || args.config.is_some() { + // read the json from file or config, and get base path if from file + let (json, base_path) = match args.manifest.as_deref() { + Some(manifest_path) => { + let base_path = std::fs::canonicalize(manifest_path)? + .parent() + .map(|p| p.to_path_buf()); + (std::fs::read_to_string(manifest_path)?, base_path) + } + None => ( + args.config.unwrap_or_default(), + std::env::current_dir().ok(), + ), + }; + + // read the signing information from the manifest definition + let mut sign_config = SignConfig::from_json(&json)?; + + // read the manifest information + let manifest_def: ManifestDef = serde_json::from_slice(json.as_bytes())?; + let mut builder = Builder::from_json(&json)?; + let mut manifest = manifest_def.manifest; + + // add claim_tool generator so we know this was created using this tool + let mut tool_generator = ClaimGeneratorInfo::new(env!("CARGO_PKG_NAME")); + tool_generator.set_version(env!("CARGO_PKG_VERSION")); + if !manifest.claim_generator_info.is_empty() + || manifest.claim_generator_info[0].name == "c2pa-rs" + { + manifest.claim_generator_info = vec![tool_generator]; + } else { + manifest.claim_generator_info.insert(1, tool_generator); + } + println!("claim generator {:?}", manifest.claim_generator_info); + // set manifest base path before ingredients so ingredients can override it + if let Some(base) = base_path.as_ref() { + builder.base_path = Some(base.clone()); + sign_config.set_base_path(base); + } + + // Add any ingredients specified as file paths + if let Some(paths) = manifest_def.ingredient_paths { + for mut path in paths { + // ingredient paths are relative to the manifest path + if let Some(base) = &base_path { + if !(path.is_absolute()) { + path = base.join(&path) + } + } + let ingredient = load_ingredient(&path)?; + builder.add_ingredient(ingredient); + } + } + + if let Some(parent_path) = args.parent { + let mut ingredient = load_ingredient(&parent_path)?; + ingredient.set_is_parent(); + builder.add_ingredient(ingredient); + } + + // If the source file has a manifest store, and no parent is specified treat the source as a parent. + // note: This could be treated as an update manifest eventually since the image is the same + let has_parent = builder.definition.ingredients.iter().any(|i| i.is_parent()); + if !has_parent && !is_fragment { + let mut source_ingredient = Ingredient::from_file(&args.path)?; + if source_ingredient.manifest_data().is_some() { + source_ingredient.set_is_parent(); + builder.add_ingredient(source_ingredient); + } + } + + if let Some(remote) = args.remote { + if args.sidecar { + builder.set_no_embed(true); + builder.set_remote_url(remote); + } else { + builder.set_remote_url(remote); + } + } else if args.sidecar { + builder.set_no_embed(true); + } + + let signer = if let Some(signer_process_name) = args.signer_path { + let cb_config = CallbackSignerConfig::new(&sign_config, args.reserve_size)?; + + let process_runner = Box::new(ExternalProcessRunner::new( + cb_config.clone(), + signer_process_name, + )); + let signer = CallbackSigner::new(process_runner, cb_config); + + Box::new(signer) + } else { + sign_config.signer()? + }; + + if let Some(output) = args.output { + // fragmented embedding + if let Some(Commands::Fragment { fragments_glob }) = &args.command { + if output.exists() && !output.is_dir() { + bail!("Output cannot point to existing file, must be a directory"); + } + + if let Some(fg) = &fragments_glob { + return sign_fragmented(&mut builder, signer.as_ref(), &args.path, fg, &output); + } else { + bail!("fragments_glob must be set"); + } + } else { + if ext_normal(&output) != ext_normal(&args.path) { + bail!("Output type must match source type"); + } + if output.exists() && !args.force { + bail!("Output already exists, use -f/force to force write"); + } + + if output.file_name().is_none() { + bail!("Missing filename on output"); + } + if output.extension().is_none() { + bail!("Missing extension output"); + } + + #[allow(deprecated)] // todo: remove when we can + builder + .sign_file(signer.as_ref(), &args.path, &output) + .context("embedding manifest")?; + + // generate a report on the output file + let reader = Reader::from_file(&output).map_err(special_errs)?; + if args.detailed { + println!("{:#?}", reader); + } else { + println!("{}", reader) + } + } + } else { + bail!("Output path required with manifest definition") + } + } else if args.parent.is_some() || args.sidecar || args.remote.is_some() { + bail!("Manifest definition required with these options or flags") + } else if let Some(output) = args.output { + if output.is_file() || output.extension().is_some() { + bail!("Output must be a folder for this option.") + } + if output.exists() { + if args.force { + remove_dir_all(&output)?; + } else { + bail!("Output already exists, use -f/force to force write"); + } + } + create_dir_all(&output)?; + if args.ingredient { + let report = Ingredient::from_file_with_folder(&args.path, &output) + .map_err(special_errs)? + .to_string(); + File::create(output.join("ingredient.json"))?.write_all(&report.into_bytes())?; + println!("Ingredient report written to the directory {:?}", &output); + } else { + let reader = Reader::from_file(&args.path).map_err(special_errs)?; + reader.to_folder(&output)?; + let report = reader.to_string(); + if args.detailed { + // for a detailed report first call the above to generate the thumbnails + // then call this to add the detailed report + let detailed = format!( + "{:#?}", + Reader::from_file(&args.path).map_err(special_errs)? + ); + File::create(output.join("detailed.json"))?.write_all(&detailed.into_bytes())?; + } + File::create(output.join("manifest_store.json"))?.write_all(&report.into_bytes())?; + println!("Manifest report written to the directory {:?}", &output); + } + } else if args.ingredient { + println!( + "{}", + Ingredient::from_file(&args.path).map_err(special_errs)? + ) + } else if args.detailed { + println!( + "{:#?}", + Reader::from_file(&args.path).map_err(special_errs)? + ) + } else if let Some(Commands::Fragment { + fragments_glob: Some(fg), + }) = &args.command + { + let stores = verify_fragmented(&args.path, fg)?; + if stores.len() == 1 { + println!("{}", stores[0]); + } else { + println!("{} Init manifests validated", stores.len()); + } + } else { + println!("{}", Reader::from_file(&args.path).map_err(special_errs)?) + } + + Ok(()) +} + +#[cfg(test)] +pub mod tests { + #![allow(clippy::unwrap_used)] + + use super::*; + + const CONFIG: &str = r#"{ + "alg": "es256", + "private_key": "es256_private.key", + "sign_cert": "es256_certs.pem", + "ta_url": "http://timestamp.digicert.com", + "assertions": [ + { + "label": "org.contentauth.test", + "data": {"my_key": "whatever I want"} + } + ] + }"#; + + #[test] + fn test_manifest_config() { + const SOURCE_PATH: &str = "tests/fixtures/earth_apollo17.jpg"; + const OUTPUT_PATH: &str = "target/tmp/unit_out.jpg"; + create_dir_all("target/tmp").expect("create_dir"); + std::fs::remove_file(OUTPUT_PATH).ok(); // remove output file if it exists + let mut builder = Builder::from_json(CONFIG).expect("from_json"); + + let signer = SignConfig::from_json(CONFIG) + .unwrap() + .set_base_path("sample") + .signer() + .expect("get_signer"); + + #[allow(deprecated)] // todo: remove when we can + let _result = builder + .sign_file(signer.as_ref(), SOURCE_PATH, OUTPUT_PATH) + .expect("embed"); + + let ms = Reader::from_file(OUTPUT_PATH) + .expect("from_file") + .to_string(); + println!("{}", ms); + //let ms = report_from_path(&OUTPUT_PATH, false).expect("report_from_path"); + assert!(ms.contains("my_key")); + } +} diff --git a/cli/src/signer.rs b/cli/src/signer.rs new file mode 100644 index 000000000..339ed4cc6 --- /dev/null +++ b/cli/src/signer.rs @@ -0,0 +1,151 @@ +// 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 std::{ + env, + path::{Path, PathBuf}, +}; + +use anyhow::{Context, Result}; +use c2pa::{create_signer, Signer, SigningAlg}; +use serde::Deserialize; + +// Pull in default certs so the binary can self config +const DEFAULT_CERTS: &[u8] = include_bytes!("../sample/es256_certs.pem"); +const DEFAULT_KEY: &[u8] = include_bytes!("../sample/es256_private.key"); + +pub fn get_ta_url() -> Option { + std::env::var("C2PA_TA_URL").ok() +} +#[derive(Debug, Default, Deserialize)] +pub struct SignConfig { + /// Signing algorithm to use - must match the associated certs + /// + /// Must be one of [ ps256 | ps384 | ps512 | es256 | es384 | es512 | ed25519 ] + /// Defaults to es256 + pub alg: Option, + /// A path to a file containing the private key required for signing + pub private_key: Option, + /// A path to a file containing the signing cert required for signing + pub sign_cert: Option, + /// A Url to a Time Authority to use when signing the manifest + pub ta_url: Option, +} + +impl SignConfig { + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).context("reading manifest configuration") + } + + // set a base for all non-absolute paths + pub fn set_base_path>(&mut self, base: P) -> &Self { + if let Some(path) = self.private_key.as_ref() { + if !path.is_absolute() { + self.private_key = Some(base.as_ref().join(path)); + } + } + if let Some(path) = self.sign_cert.as_ref() { + if !path.is_absolute() { + self.sign_cert = Some(base.as_ref().join(path)); + } + } + self + } + + pub fn signer(&self) -> Result> { + let alg = self.alg.as_deref().unwrap_or("es256").to_lowercase(); + let alg: SigningAlg = alg.parse().map_err(|_| c2pa::Error::UnsupportedType)?; + let tsa_url = self.ta_url.clone().or_else(get_ta_url); + + let mut private_key = None; + let mut sign_cert = None; + + if let Some(path) = self.private_key.as_deref() { + 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) = self.sign_cert.as_deref() { + 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 = create_signer::from_keys(&sign_cert, &private_key, alg, tsa_url) + .context("Invalid certification data")?; + return Ok(signer); + } + } + + eprintln!( + "\n\n-----------\n\n\ + Note: Using default private key and signing certificate. This is only valid for development.\n\ + A permanent key and cert should be provided in the manifest definition or in the environment variables.\n"); + + let signer = create_signer::from_keys(DEFAULT_CERTS, DEFAULT_KEY, alg, tsa_url) + .context("Invalid certification data")?; + + Ok(signer) + } +} + +#[cfg(test)] +pub mod tests { + #![allow(clippy::unwrap_used)] + + use super::*; + const CONFIG: &str = r#"{ + "alg": "es256", + "private_key": "es256_private.key", + "sign_cert": "es256_certs.pem", + "ta_url": "http://timestamp.digicert.com" + }"#; + + #[test] + fn test_sign_config() { + let mut sign_config = SignConfig::from_json(CONFIG).expect("from_json"); + sign_config.set_base_path("sample"); + + let signer = sign_config.signer().expect("get signer"); + assert_eq!(signer.alg(), SigningAlg::Es256); + } + + #[test] + fn test_sign_default() { + let sign_config = SignConfig::default(); + + let signer = sign_config.signer().expect("get signer"); + assert_eq!(signer.alg(), SigningAlg::Es256); + } + + #[test] + fn test_sign_from() { + let sign_config = SignConfig::default(); + + let signer = sign_config.signer().expect("get signer"); + assert_eq!(signer.alg(), SigningAlg::Es256); + } +} diff --git a/cli/src/tree.rs b/cli/src/tree.rs new file mode 100644 index 000000000..d0e58b206 --- /dev/null +++ b/cli/src/tree.rs @@ -0,0 +1,107 @@ +// 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::path::Path; + +use atree::{Arena, Token}; +use c2pa::{Reader, Result}; +use treeline::Tree; + +fn populate_node( + tree: &mut Arena, + reader: &Reader, + manifest_label: &str, + current_token: &Token, + name_only: bool, +) -> Result<()> { + if let Some(manifest) = reader.get_manifest(manifest_label) { + for assertion in manifest.assertions().iter() { + let label = assertion.label_with_instance(); + current_token.append(tree, format!("Assertion:{label}")); + } + + for ingredient in manifest.ingredients().iter() { + let title = ingredient.title().unwrap_or("Untitled"); + if let Some(label) = ingredient.active_manifest() { + // create new node + let data = if name_only { + format!("{}_{}", title, label) + } else { + format!("Asset:{}, Manifest:{}", title, label) + }; + + let new_token = current_token.append(tree, data); + + populate_node(tree, reader, label, &new_token, name_only)?; + } else { + let data = if name_only { + title.to_string() + } else { + format!("Asset:{title}") + }; + current_token.append(tree, data); + } + } + } + Ok(()) +} + +fn walk_tree(tree: &Arena, token: &Token) -> Tree { + let result = token.children_tokens(tree).fold( + Tree::root(tree[*token].data.clone()), + |mut root, entry_token| { + if entry_token.is_leaf(tree) { + root.push(Tree::root(tree[entry_token].data.clone())); + } else { + root.push(walk_tree(tree, &entry_token)); + } + root + }, + ); + + result +} + +/// Prints tree view of manifest store +pub fn tree>(path: P) -> Result { + let os_filename = path + .as_ref() + .file_name() + .ok_or_else(|| crate::Error::BadParam("bad filename".to_string()))?; + let asset_name = os_filename.to_string_lossy().into_owned(); + + let reader = Reader::from_file(path)?; + + // walk through the manifests and show the contents + Ok(if let Some(manifest_label) = reader.active_label() { + let data = format!("Asset:{}, Manifest:{}", asset_name, manifest_label); + let (mut tree, root_token) = Arena::with_data(data); + populate_node(&mut tree, &reader, manifest_label, &root_token, false)?; + // print tree + format!("Tree View:\n {}", walk_tree(&tree, &root_token)) + } else { + format!("Tree View:\n Asset:{asset_name}") + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tree() -> Result<()> { + let result = tree("tests/fixtures/C.jpg")?; + assert!(result.contains("Tree View:")); + assert!(result.contains("Assertion:c2pa.actions")); + Ok(()) + } +} diff --git a/cli/tests/fixtures/C.jpg b/cli/tests/fixtures/C.jpg new file mode 100644 index 000000000..0a0c6dc8f Binary files /dev/null and b/cli/tests/fixtures/C.jpg differ diff --git a/cli/tests/fixtures/do_not_train.json b/cli/tests/fixtures/do_not_train.json new file mode 100644 index 000000000..8897867d5 --- /dev/null +++ b/cli/tests/fixtures/do_not_train.json @@ -0,0 +1,23 @@ +{ + "ta_url": "http://timestamp.digicert.com", + + "claim_generator": "TestApp/0.0.1", + "claim_generator_info": [{ + "name": "Test App", + "version": "0.0.1" + }], + "assertions": [ + { + "label": "c2pa.training-mining", + "data": { + "entries": { + "c2pa.ai_generative_training": { "use": "notAllowed" }, + "c2pa.ai_inference": { "use": "notAllowed" }, + "c2pa.ai_training": { "use": "notAllowed" }, + "c2pa.data_mining": { "use": "notAllowed" } + } + } + } + ] +} + \ No newline at end of file diff --git a/cli/tests/fixtures/earth_apollo17.jpg b/cli/tests/fixtures/earth_apollo17.jpg new file mode 100644 index 000000000..4d7ec6a53 Binary files /dev/null and b/cli/tests/fixtures/earth_apollo17.jpg differ diff --git a/cli/tests/fixtures/ingredient/ingredient.json b/cli/tests/fixtures/ingredient/ingredient.json new file mode 100644 index 000000000..320a9629a --- /dev/null +++ b/cli/tests/fixtures/ingredient/ingredient.json @@ -0,0 +1,10 @@ +{ + "title": "test ingredient", + "format": "image/jpeg", + "instance_id": "xmp:iid:a60dfda7-0606-42db-a4f0-398ab4fb79cf", + "thumbnail": { + "format": "image/png", + "identifier": "../libpng-test.png" + }, + "relationship": "componentOf" + } \ No newline at end of file diff --git a/cli/tests/fixtures/ingredient_test.json b/cli/tests/fixtures/ingredient_test.json new file mode 100644 index 000000000..8484e22e0 --- /dev/null +++ b/cli/tests/fixtures/ingredient_test.json @@ -0,0 +1,132 @@ +{ + "ta_url": "http://timestamp.digicert.com", + + "claim_generator": "TestApp", + "claim_generator_info": [{ + "name": "Test App", + "version": "0.0.1", + "icon": { + "format": "image/png", + "identifier": "libpng-test.png" + } + }], + "thumbnail": { + "format": "image/jpeg", + "identifier": "earth_apollo17.jpg" + }, + "assertions": [ + { + "label": "c2pa.actions", + "data": { + "actions": [ + { + "action": "c2pa.created", + "instanceId": "xmp.iid:7b57930e-2f23-47fc-affe-0400d70b738d", + "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicMedia", + "softwareAgent": { + "name": "TestApp", + "version": "1.0", + "something": "else" + } + }, + { + "action": "c2pa.opened", + "instanceId": "xmp.iid:7b57930e-2f23-47fc-affe-0400d70b738d", + "parameters": { + "description": "import" + }, + "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicMedia", + "softwareAgent": { + "name": "TestApp", + "version": "1.0", + "icon": { + "format": "image/svg+xml", + "identifier": "sample1.svg" + }, + "something": "else" + }, + "changes": [ + { + "region" : [ + { + "type" : "temporal", + "time" : {} + }, + { + "type" : "identified", + "item" : { + "identifier" : "https://bioportal.bioontology.org/ontologies/FMA", + "value" : "lips" + } + } + ], + "description": "lip synced area" + } + ] + }, + { + "action" : "c2pa.edited", + "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/minorHumanEdits", + "changes" : [ + { + "region" : [ + { + "type" : "identified", + "item" : { + "identifier" : "track_id", + "value" : "transcript" + } + } + ] + } + ] + } + ] + } + }, + { + "label": "stds.schema-org.CreativeWork", + "data": { + "@context": "https://schema.org", + "@type": "CreativeWork", + "author": [ + { + "@type": "Person", + "name": "Joe Bloggs" + } + ] + }, + "kind": "Json" + }, + { + "label": "cawg.training-mining", + "data": { + "entries": { + "c2pa.ai_training" : { + "use" : "allowed" + }, + "c2pa.ai_generative_training" : { + "use" : "notAllowed" + } + } + } + } + ], + "ingredients": [ + { + "title": "earth_apollo17.jpg", + "format": "image/jpeg", + "document_id": "adobe:docid:photoshop:e1c344db-c371-ef45-a4fe-9b5ba6346544", + "instance_id": "xmp.iid:9867c3b4-465e-42e4-8064-76c0c0fe3d16", + "thumbnail": { + "format": "image/jpeg", + "identifier": "verify.jpeg" + }, + "relationship": "componentOf" + } + ], + "ingredient_paths": [ + "C.jpg", "ingredient/ingredient.json" + ] +} + diff --git a/cli/tests/fixtures/libpng-test.png b/cli/tests/fixtures/libpng-test.png new file mode 100644 index 000000000..c4af2adab Binary files /dev/null and b/cli/tests/fixtures/libpng-test.png differ diff --git a/cli/tests/fixtures/sample1.svg b/cli/tests/fixtures/sample1.svg new file mode 100644 index 000000000..8e3d77b77 --- /dev/null +++ b/cli/tests/fixtures/sample1.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/cli/tests/fixtures/trust/anchors.pem b/cli/tests/fixtures/trust/anchors.pem new file mode 100644 index 000000000..9e6aa6ce4 --- /dev/null +++ b/cli/tests/fixtures/trust/anchors.pem @@ -0,0 +1,113 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +wvq03sA= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/cli/tests/fixtures/trust/no-match.pem b/cli/tests/fixtures/trust/no-match.pem new file mode 100644 index 000000000..d0cb9426d --- /dev/null +++ b/cli/tests/fixtures/trust/no-match.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSDCCAfqgAwIBAgIUb+aBTX1CsjJ1iuMJ9kRudz/7qEcwBQYDK2VwMIGMMQsw +CQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEnMCUG +A1UECgweQzJQQSBUZXN0IEludGVybWVkaWF0ZSBSb290IENBMRkwFwYDVQQLDBBG +T1IgVEVTVElOR19PTkxZMRgwFgYDVQQDDA9JbnRlcm1lZGlhdGUgQ0EwHhcNMjIw +NjEwMTg0NjQxWhcNMzAwODI2MTg0NjQxWjCBgDELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUxHzAdBgNVBAoMFkMyUEEgVGVzdCBT +aWduaW5nIENlcnQxGTAXBgNVBAsMEEZPUiBURVNUSU5HX09OTFkxFDASBgNVBAMM +C0MyUEEgU2lnbmVyMCowBQYDK2VwAyEAMp5+0e83nNgQhdhBW8Rshkjy90sa1A9J +IzkItcDqCuKjeDB2MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH +AwQwDgYDVR0PAQH/BAQDAgbAMB0GA1UdDgQWBBTuLrYRqW4wu6yjIK1/iW8ud7dm +kTAfBgNVHSMEGDAWgBRXTAfC/JxQvRlk/bCbdPMDbsSfqTAFBgMrZXADQQB2R6vb +I+X8CTRC54j3NTvsUj454G1/bdzbiHVgl3n+ShOAJ85FJigE7Eoav7SeXeVnNjc8 +QZ1UrJGwgBBEP84G +-----END CERTIFICATE----- \ No newline at end of file diff --git a/sdk/src/openssl/store.cfg b/cli/tests/fixtures/trust/store.cfg similarity index 78% rename from sdk/src/openssl/store.cfg rename to cli/tests/fixtures/trust/store.cfg index bf49a13ed..d968ab695 100644 --- a/sdk/src/openssl/store.cfg +++ b/cli/tests/fixtures/trust/store.cfg @@ -1,4 +1,3 @@ - //id-kp-emailProtection 1.3.6.1.5.5.7.3.4 //id-kp-documentSigning @@ -7,5 +6,3 @@ 1.3.6.1.5.5.7.3.8 //id-kp-OCSPSigning 1.3.6.1.5.5.7.3.9 -// MS C2PA Signing -1.3.6.1.4.1.311.76.59.1.9 \ No newline at end of file diff --git a/cli/tests/fixtures/verify.jpeg b/cli/tests/fixtures/verify.jpeg new file mode 100644 index 000000000..044290ad2 Binary files /dev/null and b/cli/tests/fixtures/verify.jpeg differ diff --git a/cli/tests/integration.rs b/cli/tests/integration.rs new file mode 100644 index 000000000..9d9d45027 --- /dev/null +++ b/cli/tests/integration.rs @@ -0,0 +1,201 @@ +// 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 std::{error::Error, fs, path::PathBuf, process::Command}; + +// Add methods on commands +use assert_cmd::prelude::*; +use httpmock::{prelude::*, Mock}; +use predicate::str; +use predicates::prelude::*; + +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("tests/fixtures"); + path.push(name); + fs::canonicalize(path).expect("canonicalize") +} + +#[test] +// c2patool tests/fixtures/C.jpg trust --trust_anchors=tests/fixtures/trust/anchors.pem --trust_config=tests/fixtures/trust/store.cfg +fn tool_load_trust_settings_from_file_trusted() -> Result<(), Box> { + Command::cargo_bin("c2patool")? + .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST)) + .arg("trust") + .arg("--trust_anchors") + .arg(fixture_path("trust/anchors.pem")) + .arg("--trust_config") + .arg(fixture_path("trust/store.cfg")) + .assert() + .success() + .stdout(str::contains("C2PA Test Signing Cert")) + .stdout(str::contains("signingCredential.untrusted").not()); + Ok(()) +} + +#[test] +// c2patool tests/fixtures/C.jpg trust --trust_anchors=tests/fixtures/trust/no-match.pem --trust_config=tests/fixtures/trust/store.cfg +fn tool_load_trust_settings_from_file_untrusted() -> Result<(), Box> { + Command::cargo_bin("c2patool")? + .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST)) + .arg("trust") + .arg("--trust_anchors") + .arg(fixture_path("trust/no-match.pem")) + .arg("--trust_config") + .arg(fixture_path("trust/store.cfg")) + .assert() + .success() + .stdout(str::contains("C2PA Test Signing Cert")) + .stdout(str::contains("signingCredential.untrusted")); + Ok(()) +} + +fn create_mock_server<'a>( + server: &'a MockServer, + anchor_source: &str, + config_source: &str, +) -> Vec> { + let anchor_path = fixture_path(anchor_source).to_str().unwrap().to_owned(); + let trust_mock = server.mock(|when, then| { + when.method(GET).path("/trust/anchors.pem"); + then.status(200) + .header("content-type", "text/plain") + .body_from_file(anchor_path); + }); + let config_path = fixture_path(config_source).to_str().unwrap().to_owned(); + let config_mock = server.mock(|when, then| { + when.method(GET).path("/trust/store.cfg"); + then.status(200) + .header("content-type", "text/plain") + .body_from_file(config_path); + }); + + vec![trust_mock, config_mock] +} + +#[test] +fn tool_load_trust_settings_from_url_arg_trusted() -> Result<(), Box> { + let server = MockServer::start(); + let mocks = create_mock_server(&server, "trust/anchors.pem", "trust/store.cfg"); + + // Test flags + Command::cargo_bin("c2patool")? + .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST)) + .arg("trust") + .arg("--trust_anchors") + .arg(server.url("/trust/anchors.pem")) + .arg("--trust_config") + .arg(server.url("/trust/store.cfg")) + .assert() + .success() + .stdout(str::contains("C2PA Test Signing Cert")) + .stdout(str::contains("signingCredential.untrusted").not()); + + mocks.iter().for_each(|m| m.assert()); + + Ok(()) +} + +#[test] +fn tool_load_trust_settings_from_url_arg_untrusted() -> Result<(), Box> { + let server = MockServer::start(); + let mocks = create_mock_server(&server, "trust/no-match.pem", "trust/store.cfg"); + + Command::cargo_bin("c2patool")? + .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST)) + .arg("trust") + .arg("--trust_anchors") + .arg(server.url("/trust/anchors.pem")) + .arg("--trust_config") + .arg(server.url("/trust/store.cfg")) + .assert() + .success() + .stdout(str::contains("C2PA Test Signing Cert")) + .stdout(str::contains("signingCredential.untrusted")); + + mocks.iter().for_each(|m| m.assert()); + + Ok(()) +} + +#[test] +fn tool_load_trust_settings_from_url_env_trusted() -> Result<(), Box> { + let server = MockServer::start(); + let mocks = create_mock_server(&server, "trust/anchors.pem", "trust/store.cfg"); + + // Test flags + Command::cargo_bin("c2patool")? + .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST)) + .arg("trust") + .env("C2PATOOL_TRUST_ANCHORS", server.url("/trust/anchors.pem")) + .env("C2PATOOL_TRUST_CONFIG", server.url("/trust/store.cfg")) + .assert() + .success() + .stdout(str::contains("C2PA Test Signing Cert")) + .stdout(str::contains("signingCredential.untrusted").not()); + + mocks.iter().for_each(|m| m.assert()); + + Ok(()) +} + +#[test] +fn tool_load_trust_settings_from_url_env_untrusted() -> Result<(), Box> { + let server = MockServer::start(); + let mocks = create_mock_server(&server, "trust/no-match.pem", "trust/store.cfg"); + + // Test flags + Command::cargo_bin("c2patool")? + .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST)) + .arg("trust") + .env("C2PATOOL_TRUST_ANCHORS", server.url("/trust/anchors.pem")) + .env("C2PATOOL_TRUST_CONFIG", server.url("/trust/store.cfg")) + .assert() + .success() + .stdout(str::contains("C2PA Test Signing Cert")) + .stdout(str::contains("signingCredential.untrusted")); + + mocks.iter().for_each(|m| m.assert()); + + Ok(()) +} + +#[test] +// c2patool tests/fixtures/C.jpg --tree +fn tool_tree() -> Result<(), Box> { + Command::cargo_bin("c2patool")? + .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST)) + .arg("--tree") + .assert() + .success() + .stdout(str::contains("Asset:C.jpg, Manifest:contentauth:urn:uuid:")) + .stdout(str::contains("Assertion:c2pa.actions")); + Ok(()) +} + +#[test] +// c2patool tests/fixtures/C.jpg --info +fn tool_info() -> Result<(), Box> { + Command::cargo_bin("c2patool")? + .arg(fixture_path(TEST_IMAGE_WITH_MANIFEST)) + .arg("--info") + .assert() + .success() + .stdout(str::contains( + "Provenance URI = self#jumbf=/c2pa/contentauth:urn:uuid:", + )) + .stdout(str::contains("Manifest store size = 51217")); + Ok(()) +} diff --git a/deny.toml b/deny.toml index 08d44c878..c69a9bf7d 100644 --- a/deny.toml +++ b/deny.toml @@ -14,15 +14,14 @@ targets = [ [advisories] yanked = "deny" - ignore = [ "RUSTSEC-2021-0127", # serde_cbor - "RUSTSEC-2023-0071", # rsa Marvin Attack: (https://jira.corp.adobe.com/browse/CAI-5104), - "RUSTSEC-2024-0384", # instant (https://github.com/contentauth/c2pa-rs/issues/663) + "RUSTSEC-2023-0071", # rsa Marvin Attack: (https://jira.corp.adobe.com/browse/CAI-5104) + "RUSTSEC-2024-0399", # tokio-rustls server: https://rustsec.org/advisories/RUSTSEC-2024-0399 ] [bans] -multiple-versions = "allow" +multiple-versions = "allow" # "deny" # TODO: Re-enable when possible. [licenses] allow = [ @@ -34,8 +33,8 @@ allow = [ "LicenseRef-ring", "MIT", "MPL-2.0", - "Unicode-DFS-2016", "Unicode-3.0", + "Unicode-DFS-2016", "Zlib", ] confidence-threshold = 0.9 diff --git a/docs/usage.md b/docs/usage.md index 417857ed5..be2a0a177 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -11,7 +11,7 @@ The C2PA Rust library has been tested on the following operating systems: ## Requirements -The C2PA Rust library requires **Rust version 1.76.0** or newer. +The C2PA Rust library requires **Rust version 1.81.0** or newer. To use the library, add this to your `Cargo.toml`: @@ -42,7 +42,6 @@ The Rust library crate provides the following capabilities: * `no_interleaved_io` forces fully-synchronous I/O; otherwise, the library uses threaded I/O for some operations to improve performance. * `fetch_remote_manifests` enables the verification step to retrieve externally referenced manifest stores. External manifests are only fetched if there is no embedded manifest store and no locally adjacent .c2pa manifest store file of the same name. * `json_schema` is used by `make schema` to produce a JSON schema document that represents the `ManifestStore` data structures. -* `psxxx_ocsp_stapling_experimental` enables a demonstration feature that attempts to fetch the OCSP data from the OCSP responders listed in the manifest signing certificate. The response becomes part of the manifest and is used to prove the certificate was not revoked at the time of signing. This is only implemented for PS256, PS384 and PS512 signatures and is intended as a demonstration. * `openssl_ffi_mutex` prevents multiple threads from accessing the C OpenSSL library simultaneously. (This library is not re-entrant.) In a multi-threaded process (such as Cargo's test runner), this can lead to unpredictable behavior. ### New API diff --git a/export_schema/Cargo.toml b/export_schema/Cargo.toml index f5baf9e3b..47795c8fc 100644 --- a/export_schema/Cargo.toml +++ b/export_schema/Cargo.toml @@ -4,10 +4,10 @@ version = "0.36.1" authors = ["Dave Kozma "] license = "MIT OR Apache-2.0" edition = "2018" -rust-version = "1.76.0" +rust-version = "1.81.0" [dependencies] anyhow = "1.0.40" -c2pa = { path = "../sdk", features = ["json_schema", "unstable_api"] } +c2pa = { path = "../sdk", default-features = false, features = ["json_schema", "unstable_api"] } schemars = "0.8.21" serde_json = "1.0.117" diff --git a/internal/crypto/CHANGELOG.md b/internal/crypto/CHANGELOG.md index 9fd3291d8..a0b4346d4 100644 --- a/internal/crypto/CHANGELOG.md +++ b/internal/crypto/CHANGELOG.md @@ -6,6 +6,25 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm The format of this changelog is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.2.0](https://github.com/contentauth/c2pa-rs/compare/c2pa-crypto-v0.1.2...c2pa-crypto-v0.2.0) +_12 December 2024_ + +### Added + +* Add `RawSigner` trait to `c2pa-crypto` (derived from `c2pa::Signer`) (#716) +* Move time stamp code into c2pa-crypto (#696) + +### Fixed + +* Compile `c2pa-crypto` with `cargo check` (#768) +* Verbose assertions for `is_none()` (#704) +* Remove `c2pa::Signer` dependency on `c2pa_crypto::TimeStampProvider` (#718) +* Treat Unicode-3.0 license as approved; unpin related dependencies (#693) + +### Updated dependencies + +* Bump chrono from 0.4.38 to 0.4.39 (#763) + ## [0.1.2](https://github.com/contentauth/c2pa-rs/compare/c2pa-crypto-v0.1.1...c2pa-crypto-v0.1.2) _24 October 2024_ diff --git a/internal/crypto/Cargo.toml b/internal/crypto/Cargo.toml index 5936705aa..a98bdc982 100644 --- a/internal/crypto/Cargo.toml +++ b/internal/crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "c2pa-crypto" -version = "0.1.2" +version = "0.2.0" description = "Cryptography internals for c2pa-rs crate" authors = [ "Maurice Fisher ", @@ -18,7 +18,7 @@ readme = "README.md" keywords = ["metadata"] categories = ["api-bindings"] edition = "2021" -rust-version = "1.76.0" +rust-version = "1.81.0" exclude = ["tests/fixtures"] [package.metadata.docs.rs] @@ -30,22 +30,30 @@ json_schema = ["dep:schemars"] openssl = ["dep:openssl"] [dependencies] +asn1-rs = "0.6.2" async-generic = "1.1" async-trait = "0.1.77" base64 = "0.22.1" bcder = "0.7.3" bytes = "1.7.2" -c2pa-status-tracker = { path = "../status-tracker", version = "0.1.0" } +c2pa-status-tracker = { path = "../status-tracker", version = "0.2.0" } +ciborium = "0.2.2" +const-hex = "1.14" +coset = "0.3.1" getrandom = { version = "0.2.7", features = ["js"] } hex = "0.4.3" +nom = "7.1.3" rand = "0.8.5" -rasn = "0.18.0" -rasn-ocsp = "0.18.0" -rasn-pkix = "0.18.0" +rasn = "0.22.0" +rasn-ocsp = "0.22.0" +rasn-pkix = "0.22.0" schemars = { version = "0.8.21", optional = true } serde = { version = "1.0.197", features = ["derive"] } +serde_bytes = "0.11.5" sha1 = "0.10.6" -thiserror = "1.0.61" +sha2 = "0.10.6" +thiserror = "2.0.8" +web-time = "1.1" x509-certificate = "0.21.0" x509-parser = "0.16.0" @@ -58,19 +66,19 @@ url = "2.5.3" normal = ["openssl"] # TEMPORARY: Remove after openssl transition complete. [dependencies.chrono] -version = "0.4.38" +version = "0.4.39" default-features = false features = ["wasmbind"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies.chrono] -version = "0.4.38" +version = "0.4.39" default-features = false features = ["now", "wasmbind"] [target.'cfg(target_arch = "wasm32")'.dependencies] async-trait = { version = "0.1.77" } ecdsa = "0.16.9" -ed25519-dalek = "2.1.1" +ed25519-dalek = { version = "2.1.1", features = ["alloc", "digest", "pem", "pkcs8"] } p256 = "0.13.2" p384 = "0.13.0" rsa = { version = "0.9.6", features = ["sha2"] } @@ -85,11 +93,13 @@ web-sys = { version = "0.3.58", features = [ "Window", "WorkerGlobalScope", ] } -web-time = "1.1" [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies] getrandom = { version = "0.2.7", features = ["js"] } js-sys = "0.3.58" +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +actix = "0.13.1" + [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.31" diff --git a/internal/crypto/src/asn1/rfc3161.rs b/internal/crypto/src/asn1/rfc3161.rs index 03b89ffc3..789ee7c6e 100644 --- a/internal/crypto/src/asn1/rfc3161.rs +++ b/internal/crypto/src/asn1/rfc3161.rs @@ -24,11 +24,6 @@ use crate::asn1::{rfc4210::PkiFreeText, rfc5652::ContentInfo}; /// 1.2.840.113549.1.9.16.1.4 pub const OID_CONTENT_TYPE_TST_INFO: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 16, 1, 4]); -/// id-aa-timeStampToken -/// -/// 1.2.840.113549.1.9.16.2.14 -pub const OID_TIME_STAMP_TOKEN: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 16, 2, 14]); - /// A time-stamp request. /// /// ```ASN.1 @@ -53,6 +48,7 @@ pub struct TimeStampReq { } impl TimeStampReq { + #[allow(dead_code)] // not used on all platforms pub fn take_from(cons: &mut Constructed) -> Result> { cons.take_sequence(|cons| { let version = Integer::take_from(cons)?; @@ -149,17 +145,6 @@ impl TimeStampResp { }) }) } - - pub fn encode_ref(&self) -> impl Values + '_ { - encode::sequence(( - self.status.encode_ref(), - if let Some(time_stamp_token) = &self.time_stamp_token { - Some(time_stamp_token) - } else { - None - }, - )) - } } /// PKI status info @@ -191,16 +176,6 @@ impl PkiStatusInfo { }) }) } - - pub fn encode_ref(&self) -> impl Values + '_ { - encode::sequence(( - self.status.encode(), - self.status_string - .as_ref() - .map(|status_string| status_string.encode_ref()), - self.fail_info.as_ref().map(|fail_info| fail_info.encode()), - )) - } } /// PKI status. diff --git a/internal/crypto/src/asn1/rfc3281.rs b/internal/crypto/src/asn1/rfc3281.rs index 8b0505cb8..180088b27 100644 --- a/internal/crypto/src/asn1/rfc3281.rs +++ b/internal/crypto/src/asn1/rfc3281.rs @@ -79,6 +79,7 @@ impl AttributeCertificateInfo { } #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[allow(unused)] pub enum AttCertVersion { V2 = 1, } @@ -104,6 +105,7 @@ pub struct Holder { } #[derive(Clone, Debug, Eq, PartialEq)] +#[allow(unused)] pub enum DigestedObjectType { PublicKey = 0, PublicKeyCert = 1, @@ -142,6 +144,7 @@ pub struct ObjectDigestInfo { /// } /// ``` #[derive(Clone, Debug, Eq, PartialEq)] +#[allow(unused)] pub enum AttCertIssuer { V1Form(GeneralNames), V2Form(Box), diff --git a/internal/crypto/src/asn1/rfc4210.rs b/internal/crypto/src/asn1/rfc4210.rs index 39022baaa..93dc3719d 100644 --- a/internal/crypto/src/asn1/rfc4210.rs +++ b/internal/crypto/src/asn1/rfc4210.rs @@ -8,7 +8,6 @@ use bcder::{ decode::{Constructed, DecodeError, Source}, - encode::{self, Values}, Tag, Utf8String, }; @@ -27,10 +26,6 @@ impl PkiFreeText { cons.take_opt_sequence(|cons| Self::from_sequence(cons)) } - pub fn take_from(cons: &mut Constructed) -> Result> { - cons.take_sequence(|cons| Self::from_sequence(cons)) - } - pub fn from_sequence( cons: &mut Constructed, ) -> Result> { @@ -44,8 +39,4 @@ impl PkiFreeText { Ok(Self(res)) } - - pub fn encode_ref(&self) -> impl Values + '_ { - encode::sequence(encode::slice(&self.0, |x| x.clone().encode())) - } } diff --git a/internal/crypto/src/asn1/rfc5652.rs b/internal/crypto/src/asn1/rfc5652.rs index 5032f15e8..ba8883d1b 100644 --- a/internal/crypto/src/asn1/rfc5652.rs +++ b/internal/crypto/src/asn1/rfc5652.rs @@ -29,43 +29,11 @@ use x509_certificate::{asn1time::*, rfc3280::*, rfc5280::*, rfc5652::*}; use crate::asn1::rfc3281::AttributeCertificate; -/// The data content type. -/// -/// `id-data` in the specification. -/// -/// 1.2.840.113549.1.7.1 -pub const OID_ID_DATA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 7, 1]); - /// The signed-data content type. /// /// 1.2.840.113549.1.7.2 pub const OID_ID_SIGNED_DATA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 7, 2]); -/// Enveloped data content type. -/// -/// 1.2.840.113549.1.7.3 -pub const OID_ENVELOPE_DATA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 7, 3]); - -/// Digested-data content type. -/// -/// 1.2.840.113549.1.7.5 -pub const OID_DIGESTED_DATA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 7, 5]); - -/// Encrypted-data content type. -/// -/// 1.2.840.113549.1.7.6 -pub const OID_ENCRYPTED_DATA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 7, 6]); - -/// Authenticated-data content type. -/// -/// 1.2.840.113549.1.9.16.1.2 -pub const OID_AUTHENTICATED_DATA: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 16, 1, 2]); - -/// Identifies the content-type attribute. -/// -/// 1.2.840.113549.1.9.3 -pub const OID_CONTENT_TYPE: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 3]); - /// Identifies the message-digest attribute. /// /// 1.2.840.113549.1.9.4 @@ -76,11 +44,6 @@ pub const OID_MESSAGE_DIGEST: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, /// 1.2.840.113549.1.9.5 pub const OID_SIGNING_TIME: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 5]); -/// Identifies the countersignature attribute. -/// -/// 1.2.840.113549.1.9.6 -pub const OID_COUNTER_SIGNATURE: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 6]); - /// Content info. /// /// ```ASN.1 @@ -158,23 +121,6 @@ pub struct SignedData { } impl SignedData { - /// Attempt to decode BER encoded bytes to a parsed data structure. - pub fn decode_ber(data: &[u8]) -> Result> { - Constructed::decode(data, bcder::Mode::Ber, Self::decode) - } - - pub fn decode(cons: &mut Constructed) -> Result> { - cons.take_sequence(|cons| { - let oid = Oid::take_from(cons)?; - - if oid != OID_ID_SIGNED_DATA { - return Err(cons.content_err("expected signed data OID")); - } - - cons.take_constructed_if(Tag::CTX_0, Self::take_from) - }) - } - pub fn take_from(cons: &mut Constructed) -> Result> { cons.take_sequence(|cons| { let version = CmsVersion::take_from(cons)?; @@ -197,25 +143,6 @@ impl SignedData { }) }) } - - pub fn encode_ref(&self) -> impl Values + '_ { - encode::sequence(( - OID_ID_SIGNED_DATA.encode_ref(), - encode::sequence_as( - Tag::CTX_0, - encode::sequence(( - self.version.encode(), - self.digest_algorithms.encode_ref(), - self.content_info.encode_ref(), - self.certificates - .as_ref() - .map(|certs| certs.encode_ref_as(Tag::CTX_0)), - // TODO crls. - self.signer_infos.encode_ref(), - )), - ), - )) - } } /// Digest algorithm identifiers. @@ -252,10 +179,6 @@ impl DigestAlgorithmIdentifiers { Ok(Self(identifiers)) }) } - - pub fn encode_ref(&self) -> impl Values + '_ { - encode::set(&self.0) - } } pub type DigestAlgorithmIdentifier = AlgorithmIdentifier; @@ -294,10 +217,6 @@ impl SignerInfos { Ok(Self(infos)) }) } - - pub fn encode_ref(&self) -> impl Values + '_ { - encode::set(&self.0) - } } /// Encapsulated content info. @@ -343,15 +262,6 @@ impl EncapsulatedContentInfo { }) }) } - - pub fn encode_ref(&self) -> impl Values + '_ { - encode::sequence(( - self.content_type.encode_ref(), - self.content - .as_ref() - .map(|content| encode::sequence_as(Tag::CTX_0, content.encode_ref())), - )) - } } /// Per-signer information. @@ -647,10 +557,6 @@ impl DerefMut for SignedAttributes { } impl SignedAttributes { - pub fn take_from(cons: &mut Constructed) -> Result> { - cons.take_set(|cons| Self::take_from_set(cons)) - } - pub fn take_from_set( cons: &mut Constructed, ) -> Result> { @@ -759,10 +665,6 @@ impl DerefMut for UnsignedAttributes { } impl UnsignedAttributes { - pub fn take_from(cons: &mut Constructed) -> Result> { - cons.take_set(|cons| Self::take_from_set(cons)) - } - pub fn take_from_set( cons: &mut Constructed, ) -> Result> { @@ -846,6 +748,8 @@ pub type UnprotectedAttributes = Vec; /// ori [4] OtherRecipientInfo } /// ``` #[derive(Clone, Debug, Eq, PartialEq)] +#[allow(unused)] +#[allow(clippy::enum_variant_names)] pub enum RecipientInfo { KeyTransRecipientInfo(KeyTransRecipientInfo), KeyAgreeRecipientInfo(KeyAgreeRecipientInfo), @@ -881,6 +785,7 @@ pub struct KeyTransRecipientInfo { /// subjectKeyIdentifier [0] SubjectKeyIdentifier } /// ``` #[derive(Clone, Debug, Eq, PartialEq)] +#[allow(unused)] pub enum RecipientIdentifier { IssuerAndSerialNumber(IssuerAndSerialNumber), SubjectKeyIdentifier(SubjectKeyIdentifier), @@ -914,6 +819,7 @@ pub struct KeyAgreeRecipientInfo { /// originatorKey [1] OriginatorPublicKey } /// ``` #[derive(Clone, Debug, Eq, PartialEq)] +#[allow(unused)] pub enum OriginatorIdentifierOrKey { IssuerAndSerialNumber(IssuerAndSerialNumber), SubjectKeyIdentifier(SubjectKeyIdentifier), @@ -957,6 +863,7 @@ pub struct RecipientEncryptedKey { /// rKeyId [0] IMPLICIT RecipientKeyIdentifier } /// ``` #[derive(Clone, Debug, Eq, PartialEq)] +#[allow(unused)] pub enum KeyAgreeRecipientIdentifier { IssuerAndSerialNumber(IssuerAndSerialNumber), RKeyId(RecipientKeyIdentifier), @@ -1135,6 +1042,7 @@ impl RevocationInfoChoices { /// other [1] IMPLICIT OtherRevocationInfoFormat } /// ``` #[derive(Clone, Debug, Eq, PartialEq)] +#[allow(unused)] pub enum RevocationInfoChoice { Crl(Box), Other(OtherRevocationInfoFormat), @@ -1268,10 +1176,6 @@ impl CertificateSet { Ok(Self(certs)) } - - pub fn encode_ref_as(&self, tag: Tag) -> impl Values + '_ { - encode::set_as(tag, &self.0) - } } /// Issuer and serial number. @@ -1372,10 +1276,6 @@ pub struct OtherKeyAttribute { pub type ContentType = Oid; -pub type MessageDigest = OctetString; - -pub type SigningTime = Time; - /// Time variant. /// /// ```ASN.1 @@ -1416,6 +1316,4 @@ impl From