diff --git a/Cargo.toml b/Cargo.toml index ddffa90..5b3445c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,27 @@ [package] -name = "c2pa-python" +name = "c2pa-go" version = "0.5.0" edition = "2021" -authors = ["Gavin Peacock "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] name = "c2pa" -crate-type = ["cdylib"] - +crate-type = ["staticlib"] [dependencies] -c2pa = {version = "0.32.0", features = ["unstable_api", "openssl"]} +c2pa = { git = "https://git.stream.place/aquareum-tv/c2pa-rs.git", rev = "5ca8c085a05daa36a91fb9edfdee6098cffac69b", features = [ + "unstable_api", + "openssl", +] } +# c2pa = { path = "../c2pa-rs/sdk", features = ["unstable_api", "openssl"] } +# c2pa = { version = "0.33.3", features = ["unstable_api", "openssl"] } pem = "3.0.3" serde = { version = "1.0.197", features = ["derive"] } serde_derive = "1.0" serde_json = "1.0" thiserror = "1.0.49" -uniffi = "0.24.1" +uniffi = "= 0.25.0" [build-dependencies] -uniffi = { version = "0.24.1", features = ["build"] } +uniffi = { version = "= 0.25.0", features = ["build"] } diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..70865b9 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ + +.PHONY: all +all: rust types go + +.PHONY: rust +rust: + cargo install uniffi-bindgen-go --git https://github.com/NordSecurity/uniffi-bindgen-go --tag v0.2.1+v0.25.0 + cargo build --release + +.PHONY: types +types: + go install github.com/atombender/go-jsonschema@latest + cargo install --git https://github.com/aquareum-tv/c2pa-rs export_schema + export_schema + go-jsonschema --only-models -p manifeststore ./target/schema/ManifestStore.schema.json -o pkg/c2pa/generated/manifeststore/manifeststore.go + go-jsonschema --only-models -p manifestdefinition ./target/schema/ManifestDefinition.schema.json -o pkg/c2pa/generated/manifestdefinition/manifestdefinition.go + go-jsonschema --only-models -p settings ./target/schema/Settings.schema.json -o pkg/c2pa/generated/settings/settings.go + +.PHONY: go +go: + mkdir -p dist + uniffi-bindgen-go src/c2pa.udl --out-dir pkg/c2pa/generated + go build -a -o ./dist/go-demo ./pkg/c2pa/demo/... + +# need es256k-enabled c2patool +.PHONY: test +test: + cargo install --git https://git.stream.place/aquareum-tv/c2patool.git + go test ./pkg/... \ No newline at end of file diff --git a/README.md b/README.md index f668bb5..92fac1f 100644 --- a/README.md +++ b/README.md @@ -1,335 +1,49 @@ -# C2PA Python +# C2PA Go -Python bindings for the C2PA Content Authenticity Initiative (CAI) library. +This is a proof-of-concept of Golang bindings to the C2PA library; it's forked from the Python repo. -This library enables you to read and validate C2PA data in supported media files and add signed manifests to supported media files. +### Building -**NOTE**: This is a completely different API from 0.4.0. Check [Release notes](#release-notes) for changes. +You'll need cargo and go. -**WARNING**: This is an prerelease version of this library. There may be bugs and unimplemented features, and the API is subject to change. - -## Installation - -Install from PyPI by entering this command: - -``` -pip install -U c2pa-python -``` - -This is a platform wheel built with Rust that works on Windows, macOS, and most Linux distributions (using [manylinux](https://github.com/pypa/manylinux)). If you need to run on another platform, see [Development](#development) for information on how to build from source. - -### Reinstalling - -If you tried unsuccessfully to install this package before the [0.40 release](https://github.com/contentauth/c2pa-python/releases/tag/v0.4), then use this command to reinstall: - -``` -pip install --upgrade --force-reinstall c2pa-python -``` - -## Usage - -### Import - -Import the API as follows: - -```py -from c2pa import * -``` - -### Read and validate C2PA data in a file or stream - -Use the `Reader` to read C2PA data from the specified file. -This examines the specified media file for C2PA data and generates a report of any data it finds. If there are validation errors, the report includes a `validation_status` field. For a summary of supported media types, see [Supported file formats](#supported-file-formats). - -A media file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map. - -The manifests may contain binary resources such as thumbnails which can be retrieved with `resource_to_stream` or `resource_to_file` using the associated `identifier` field values and a `uri`. - -NOTE: For a comprehensive reference to the JSON manifest structure, see the [Manifest store reference](https://opensource.contentauthenticity.org/docs/manifest/manifest-ref). -```py -try: - # Create a reader from a file path - reader = c2pa.Reader.from_file("path/to/media_file.jpg") - # It's also possible to create a reader from a format and stream - # Note that these two readers are functionally equivalent - stream = open("path/to/media_file.jpg", "rb") - reader = c2pa.Reader("image/jpeg", stream) - - # Print the JSON for a manifest. - print("manifest store:", reader.json()) - - # Get the active manifest. - manifest = reader.get_active_manifest() - if manifest != None: - - # get the uri to the manifest's thumbnail and write it to a file - uri = manifest["thumbnail"]["identifier"] - reader.resource_to_file(uri, "thumbnail_v2.jpg") - -except Exception as err: - print(err) ``` +make +[ ... ] -### Add a signed manifest to a media file or stream - -Use a `Builder` to add a manifest to an asset. - -```py -try: - # Define a function to sign the claim bytes - # In this case we are using a pre-defined sign_ps256 method, passing in our private cert - # Normally this cert would be kept safe in some other location - def private_sign(data: bytes) -> bytes: - return sign_ps256(data, "tests/fixtures/ps256.pem") - - # read our public certs into memory - certs = open(data_dir + "ps256.pub", "rb").read() - - # Create a signer from the private signer, certs and a time stamp service url - signer = create_signer(private_sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") - - # Define a manifest with thumbnail and an assertion. - manifest_json = { - "claim_generator_info": [{ - "name": "python_test", - "version": "0.1" - }], - "title": "Do Not Train Example", +./dist/go-demo ~/testvids/screenshot-signed.jpg +{ + "active_manifest": "urn:uuid:019cfdc5-8d91-4b2a-bb0d-f94afb9badef", + "manifests": { + "urn:uuid:019cfdc5-8d91-4b2a-bb0d-f94afb9badef": { + "claim_generator": "Aquareum c2patool/0.9.6 c2pa-rs/0.33.1", + "title": "Video File", + "format": "image/jpeg", + "instance_id": "xmp:iid:755324ba-0c38-4d9a-b7c1-be8a41035ac6", "thumbnail": { - "format": "image/jpeg", - "identifier": "thumbnail" + "format": "image/jpeg", + "identifier": "self#jumbf=c2pa.assertions/c2pa.thumbnail.claim.jpeg" }, + "ingredients": [], "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" } + { + "label": "c2pa.actions", + "data": { + "actions": [ + { + "action": "c2pa.published" + } + ] } } - } - ] - } - - # Create a builder add a thumbnail resource and an ingredient file. - builder = Builder(manifest_json) - - # The uri provided here "thumbnail" must match an identifier in the manifest definition. - builder.add_resource_file("thumbnail", "tests/fixtures/A_thumbnail.jpg") - - # Or add the resource from a stream - a_thumbnail_jpg_stream = open("tests/fixtures/A_thumbnail.jpg", "rb") - builder.add_resource("image/jpeg", a_thumbnail_jpg_stream) - - # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail - ingredient_json = { - "title": "A.jpg", - "relationship": "parentOf", # "parentOf", "componentOf" or "inputTo" - "thumbnail": { - "identifier": "thumbnail", - "format": "image/jpeg" + ], + "signature_info": { + "alg": "Es256", + "issuer": "Internet Widgits Pty Ltd", + "cert_serial_number": "421347483195564801015437680009080750481212048692", + "time": "2024-08-08T17:36:42+00:00" + }, + "label": "urn:uuid:019cfdc5-8d91-4b2a-bb0d-f94afb9badef" } } - - # Add the ingredient to the builder loading information from a source file. - builder.add_ingredient_file(ingredient_json, "tests/fixtures/A.jpg") - - # Or add the ingredient from a stream - a_jpg_stream = open("tests/fixtures/A.jpg", "rb") - builder.add_ingredient("image/jpeg", a_jpg_stream) - - # At this point we could archive or unarchive our Builder to continue later. - # In this example we use a bytearray for the archive stream. - # all ingredients and resources will be saved in the archive - archive = io.BytesIO(bytearray()) - builder.to_archive(archive) - archive.seek() - builder = builder.from_archive(archive) - - # Sign and add our manifest to a source file, writing it to an output file. - # This returns the binary manifest data that could be uploaded to cloud storage. - c2pa_data = builder.sign_file(signer, "tests/fixtures/A.jpg", "target/out.jpg") - - # Or sign the builder with a stream and output it to a stream - input_stream = open("tests/fixtures/A.jpg", "rb") - output_stream = open("target/out.jpg", "wb") - c2pa_data = builder.sign(signer, "image/jpeg", input_stream, output_stream) - -except Exception as err: - print(err) - ``` - -### Creating a manifest JSON definition file - -The manifest JSON string defines the C2PA manifest to add to the file. - -```py -manifest_json = json.dumps({ - "claim_generator": "python_test/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" } - } - } - } - ] - }) -``` - -## Supported file formats - - | Extensions | MIME type | - | ------------- | --------------------------------------------------- | - | `avi` | `video/msvideo`, `video/avi`, `application-msvideo` | - | `avif` | `image/avif` | - | `c2pa` | `application/x-c2pa-manifest-store` | - | `dng` | `image/x-adobe-dng` | - | `heic` | `image/heic` | - | `heif` | `image/heif` | - | `jpg`, `jpeg` | `image/jpeg` | - | `m4a` | `audio/mp4` | - | `mp4` | `video/mp4`, `application/mp4` | - | `mov` | `video/quicktime` | - | `png` | `image/png` | - | `svg` | `image/svg+xml` | - | `tif`,`tiff` | `image/tiff` | - | `wav` | `audio/x-wav` | - | `webp` | `image/webp` | - - -## Development - -It is best to [set up a virtual environment](https://virtualenv.pypa.io/en/latest/installation.html) for development and testing. To build from source on Linux, install `curl` and `rustup` then set up Python. - -First update `apt` then (if needed) install `curl`: - -``` -apt update -apt install curl -``` - -Install Rust: - -``` -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source "$HOME/.cargo/env" -``` - -Install Python, `pip`, and `venv`: - -``` -apt install python3 -apt install pip -apt install python3.11-venv -python3 -m venv .venv -``` - -Build the wheel for your platform: - -``` -source .venv/bin/activate -pip install maturin -pip install uniffi-bindgen -python3 -m pip install build -pip install -U pytest - -python3 -m build --wheel -``` - -### ManyLinux build - -Build using [manylinux](https://github.com/pypa/manylinux) by using a Docker image as follows: - -``` -docker run -it quay.io/pypa/manylinux_2_28_aarch64 bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source "$HOME/.cargo/env" -export PATH=/opt/python/cp312-cp312/bin:$PATH -pip install maturin -pip install venv -pip install build -pip install -U pytest - -cd home -git clone https://github.com/contentauth/c2pa-python.git -cd c2pa-python -python3 -m build --wheel -auditwheel repair target/wheels/c2pa_python-0.4.0-py3-none-linux_aarch64.whl -``` - -### Testing - -We use [PyTest](https://docs.pytest.org/) for testing. - -Run tests by entering this command: - -``` -source .venv/bin/activate -maturin develop -pytest -deactivate -``` - -For example: - -``` -source .venv/bin/activate -maturin develop -python3 tests/training.py -deactivate -``` - -## Release notes - -### Version 0.5.0 - -- This release rewrites the API to be stream based using a Builder and Reader model. -- The functions now support throwing c2pa.Error values, caught with try/except. -- Instead of `c2pa.read_file` you now call `c2pa_api.Reader.from_file` and `reader.json`. -- Read thumbnails and other resources use `reader.resource_to_stream` or `reader.resource.to_file`. -- Instead of `c2pa.sign_file` use `c2pa_api.Builder.from_json` and `builder.sign` or `builder.sign_file`. -- Add thumbnails or other resources with `builder.add_resource` or `builder.add_resource_file`. -- Add Ingredients with `builder.add_ingredient` or `builder.add_ingredient_file`. -- You can archive a `Builder` using `builder.to_archive` and reconstruct it with `builder.from_archive`. -- Signers can be constructed with `c2pa_api.create_signer`. -- The signer now requires a signing function to keep private keys private. -- Example signing functions are provided in c2pa_api.py - -### Version 0.4.0 - -This release: - -- Changes the name of the import from `c2pa-python` to `c2pa`. -- Supports pre-built platform wheels for macOS, Windows and [manylinux](https://github.com/pypa/manylinux). - -### Version 0.3.0 - -This release includes some breaking changes to align with future APIs: -- `C2paSignerInfo` moves the `alg` to the first parameter from the 3rd. -- `c2pa.verify_from_file_json` is now `c2pa.read_file`. -- `c2pa.ingredient_from_file_json` is now `c2pa.read_ingredient_file`. -- `c2pa.add_manifest_to_file_json` is now `c2pa.sign_file`. -- There are many more specific errors types now, and Error messages always start with the name of the error i.e (str(err.value).startswith("ManifestNotFound")). -- The ingredient thumbnail identifier may be jumbf uri reference if a valid thumb already exists in the active manifest. -- Extracted file paths for read_file now use a folder structure and different naming conventions. - -## License - -This package is distributed under the terms of both the [MIT license](https://github.com/contentauth/c2pa-python/blob/main/LICENSE-MIT) and the [Apache License (Version 2.0)](https://github.com/contentauth/c2pa-python/blob/main/LICENSE-APACHE). - -Note that some components and dependent crates are licensed under different terms; please check the license terms for each crate and component for details. - -### 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-python/blob/main/CONTRIBUTING.md). - - +} +``` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ef6766f --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module git.stream.place/streamplace/c2pa-go + +go 1.22.2 + +require ( + github.com/decred/dcrd/dcrec/secp256k1 v1.0.4 + github.com/stretchr/testify v1.9.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4636228 --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= +github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1 v1.0.4 h1:0XErmfJBiVbl0NvyclGn4jr+1hIylDf5beFi9W0o7Fc= +github.com/decred/dcrd/dcrec/secp256k1 v1.0.4/go.mod h1:00z7mJdugt+GBAzPN1QrDRGCXxyKUiexEHu6ukxEw3k= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0 h1:3GIJYXQDAKpLEFriGFN8SbSffak10UXHGdIcFaMPykY= +github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0/go.mod h1:3s92l0paYkZoIHuj4X93Teg/HB7eGM9x/zokGw+u4mY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..6bd3bc0 --- /dev/null +++ b/meson.build @@ -0,0 +1,27 @@ +project( + 'c2pa-go', + default_options: ['default_library=static'], +) + +prog_cargo = find_program('cargo') + +cargo_cmd = '@0@ build --release --target-dir "@1@"'.format(prog_cargo.full_path(), meson.current_build_dir()) +archive_dir = meson.current_build_dir() + +triple = get_option('RUST_TRIPLE') +if triple != '' + cargo_cmd += ' --target @0@'.format(triple) + archive_dir += '/' + triple +endif + +c2pa_go_dep = custom_target( + 'libc2pa', + input: [], + output: ['libc2pa.a'], + command: [ + 'sh', + '-c', + 'cd "@2@" && @0@ && cp "@3@/release/libc2pa.a" "@1@/libc2pa.a"'.format(cargo_cmd, meson.current_build_dir(), meson.current_source_dir(), archive_dir), + ], + build_by_default: true, +) diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..b3093e0 --- /dev/null +++ b/meson.options @@ -0,0 +1 @@ +option('RUST_TRIPLE', type : 'string', description : 'Rust triple for cross-compilation') \ No newline at end of file diff --git a/pkg/c2pa/algorithms.go b/pkg/c2pa/algorithms.go new file mode 100644 index 0000000..6daecf7 --- /dev/null +++ b/pkg/c2pa/algorithms.go @@ -0,0 +1,76 @@ +package c2pa + +import ( + "crypto" + "crypto/rsa" + "fmt" + + rustC2PA "git.stream.place/streamplace/c2pa-go/pkg/c2pa/generated/c2pa" +) + +type SigningAlgorithmName string + +const ( + ED25519 SigningAlgorithmName = "ed25519" + ES256 SigningAlgorithmName = "es256" + ES256K SigningAlgorithmName = "es256k" + ES384 SigningAlgorithmName = "es384" + ES512 SigningAlgorithmName = "es512" + PS256 SigningAlgorithmName = "ps256" + PS384 SigningAlgorithmName = "ps384" + PS512 SigningAlgorithmName = "ps512" +) + +type SigningAlgorithm struct { + Name SigningAlgorithmName + RustC2PAType rustC2PA.SigningAlg + Hash crypto.Hash +} + +func GetSigningAlgorithm(algStr string) (*SigningAlgorithm, error) { + alg := SigningAlgorithmName(algStr) + switch alg { + case ED25519: + return &SigningAlgorithm{ED25519, rustC2PA.SigningAlgEd25519, crypto.Hash(0)}, nil + case ES256: + return &SigningAlgorithm{ES256, rustC2PA.SigningAlgEs256, crypto.SHA256}, nil + case ES256K: + return &SigningAlgorithm{ES256K, rustC2PA.SigningAlgEs256k, crypto.SHA256}, nil + case ES384: + return &SigningAlgorithm{ES384, rustC2PA.SigningAlgEs384, crypto.SHA384}, nil + case ES512: + return &SigningAlgorithm{ES512, rustC2PA.SigningAlgEs512, crypto.SHA512}, nil + case PS256: + return &SigningAlgorithm{PS256, rustC2PA.SigningAlgPs256, crypto.SHA256}, nil + case PS384: + return &SigningAlgorithm{PS384, rustC2PA.SigningAlgPs384, crypto.SHA384}, nil + case PS512: + return &SigningAlgorithm{PS512, rustC2PA.SigningAlgPs512, crypto.SHA512}, nil + default: + return nil, fmt.Errorf("algorithm not found: %s", alg) + } +} + +// get digest and crypto options for passing to the actual signer +func (alg *SigningAlgorithm) Digest(data []byte) ([]byte, crypto.SignerOpts, error) { + switch alg.Name { + case ED25519: + // ed25519 handles its own hashing + return data, alg.Hash, nil + case ES256, ES256K, ES384, ES512: + h := alg.Hash.New() + h.Write(data) + digest := h.Sum(nil) + return digest, alg.Hash, nil + case PS256, PS384, PS512: + h := alg.Hash.New() + h.Write(data) + digest := h.Sum(nil) + opts := &rsa.PSSOptions{ + Hash: alg.Hash, + SaltLength: rsa.PSSSaltLengthEqualsHash, + } + return digest, opts, nil + } + return nil, nil, fmt.Errorf("unknown algorithm: %s", alg.Name) +} diff --git a/pkg/c2pa/c2pa.go b/pkg/c2pa/c2pa.go new file mode 100644 index 0000000..590be25 --- /dev/null +++ b/pkg/c2pa/c2pa.go @@ -0,0 +1,160 @@ +package c2pa + +import ( + "crypto" + "encoding/json" + "fmt" + "io" + "mime" + "os" + "path/filepath" + + rustC2PA "git.stream.place/streamplace/c2pa-go/pkg/c2pa/generated/c2pa" + "git.stream.place/streamplace/c2pa-go/pkg/c2pa/generated/manifestdefinition" + "git.stream.place/streamplace/c2pa-go/pkg/c2pa/generated/manifeststore" +) + +// #cgo LDFLAGS: -L../../target/release -lc2pa -lm +// #cgo darwin LDFLAGS: -framework Security +import "C" + +type Reader interface { + GetManifest(label string) *manifeststore.Manifest + GetActiveManifest() *manifeststore.Manifest + GetProvenanceCertChain() string +} + +func FromStream(target io.ReadSeeker, mType string) (Reader, error) { + stream := C2PAStreamReader{target} + r := rustC2PA.NewReader() + r.FromStream(mType, &stream) + ret, err := r.Json() + if err != nil { + return nil, err + } + certs, err := r.GetProvenanceCertChain() + if err != nil { + return nil, err + } + var store manifeststore.ManifestStoreSchemaJson + err = json.Unmarshal([]byte(ret), &store) + if err != nil { + return nil, err + } + if len(store.ValidationStatus) > 0 { + errBs, err := json.Marshal(store.ValidationStatus) + if err != nil { + return nil, err + } + return &C2PAReader{store: &store, certs: certs}, fmt.Errorf("validation error: %s", string(errBs)) + } + return &C2PAReader{store: &store, certs: certs}, nil +} + +func FromFile(fname string) (Reader, error) { + mType := mime.TypeByExtension(filepath.Ext(fname)) + if mType == "" { + return nil, fmt.Errorf("couldn't find MIME type for filename %s", fname) + } + f, err := os.Open(fname) + if err != nil { + return nil, err + } + defer f.Close() + return FromStream(f, mType) +} + +type C2PAReader struct { + store *manifeststore.ManifestStoreSchemaJson + certs string +} + +func (r *C2PAReader) GetManifest(label string) *manifeststore.Manifest { + m, ok := r.store.Manifests[label] + if !ok { + return nil + } + return &m +} + +func (r *C2PAReader) GetActiveManifest() *manifeststore.Manifest { + if r.store.ActiveManifest == nil { + return nil + } + return r.GetManifest(*r.store.ActiveManifest) +} + +func (r *C2PAReader) GetProvenanceCertChain() string { + return r.certs +} + +type Builder interface { + Sign(input io.ReadSeeker, output io.ReadWriteSeeker, mimeType string) error + SignFile(infile, outfile string) error +} + +type BuilderParams struct { + Cert []byte + Signer crypto.Signer + TAURL string + Algorithm *SigningAlgorithm +} + +type C2PABuilder struct { + builder *rustC2PA.Builder + manifest *ManifestDefinition + params *BuilderParams +} + +type ManifestDefinition manifestdefinition.ManifestDefinitionSchemaJson + +func NewBuilder(manifest *ManifestDefinition, params *BuilderParams) (Builder, error) { + b := rustC2PA.NewBuilder() + bs, err := json.Marshal(manifest) + if err != nil { + return nil, err + } + err = b.WithJson(string(bs)) + if err != nil { + return nil, err + } + + return &C2PABuilder{builder: b, manifest: manifest, params: params}, nil +} + +func (b *C2PABuilder) Sign(input io.ReadSeeker, output io.ReadWriteSeeker, mimeType string) error { + mySigner := &C2PACallbackSigner{ + signer: b.params.Signer, + algorithm: *b.params.Algorithm, + } + signer := rustC2PA.NewCallbackSigner(mySigner, b.params.Algorithm.RustC2PAType, b.params.Cert, &b.params.TAURL) + _, err := b.builder.Sign(mimeType, &C2PAStreamReader{input}, &C2PAStreamWriter{output}, signer) + if err != nil { + return err + } + _, err = FromStream(output, mimeType) + if err != nil { + return fmt.Errorf("unable to validate newly-signed file: %w", err) + } + return nil +} + +// helper function for operating on files +func (b *C2PABuilder) SignFile(infile, outfile string) error { + mimeType := mime.TypeByExtension(filepath.Ext(infile)) + if mimeType == "" { + return fmt.Errorf("couldn't find MIME type for filename %s", infile) + } + input, err := os.Open(infile) + if err != nil { + return err + } + defer input.Close() + + output, err := os.Create(outfile) + if err != nil { + return err + } + defer output.Close() + return b.Sign(input, output, mimeType) +} diff --git a/pkg/c2pa/c2pa_test.go b/pkg/c2pa/c2pa_test.go new file mode 100644 index 0000000..8143015 --- /dev/null +++ b/pkg/c2pa/c2pa_test.go @@ -0,0 +1,165 @@ +package c2pa + +import ( + "encoding/json" + "fmt" + "os/exec" + "path/filepath" + "runtime" + "testing" + "time" + + "os" + + "github.com/stretchr/testify/require" +) + +func getFixtures() string { + _, filename, _, _ := runtime.Caller(0) + dir := filepath.Dir(filename) + return filepath.Join(dir, "..", "..", "tests", "fixtures") +} + +func getPair(name string) ([]byte, []byte, error) { + fixtures := getFixtures() + + certBytes, err := os.ReadFile(filepath.Join(fixtures, fmt.Sprintf("%s_certs.pem", name))) + if err != nil { + return nil, nil, err + } + keyBytes, err := os.ReadFile(filepath.Join(fixtures, fmt.Sprintf("%s_private.key", name))) + if err != nil { + return nil, nil, err + } + return certBytes, keyBytes, nil +} + +func TestSigning(t *testing.T) { + tests := []struct { + name string + }{ + {"es256"}, + {"es256k"}, + {"es384"}, + {"es512"}, + {"ed25519"}, + {"ps256"}, + {"ps384"}, + {"ps512"}, + } + + dname, err := os.MkdirTemp("", "c2pa-go-test") + require.NoError(t, err) + // defer os.RemoveAll(dname) + fmt.Printf("writing to %s\n", dname) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + certBytes, keyBytes, err := getPair(test.name) + require.NoError(t, err) + manifestBs := []byte(` + { + "title": "Image File", + "assertions": [ + { + "label": "c2pa.actions", + "data": { "actions": [{ "action": "c2pa.published" }] } + } + ] + } + `) + var manifest ManifestDefinition + err = json.Unmarshal(manifestBs, &manifest) + require.NoError(t, err) + alg, err := GetSigningAlgorithm(test.name) + require.NoError(t, err) + signer, err := MakeStaticSigner(keyBytes) + require.NoError(t, err) + b, err := NewBuilder(&manifest, &BuilderParams{ + Cert: certBytes, + Signer: signer, + Algorithm: alg, + TAURL: "http://timestamp.digicert.com", + }) + require.NoError(t, err) + + fixtures := getFixtures() + for _, fname := range []string{"A.jpg", "video.mp4"} { + input := filepath.Join(fixtures, fname) + output := filepath.Join(dname, fmt.Sprintf("signed-%s-%s", test.name, fname)) + + err = b.SignFile(input, output) + require.NoError(t, err) + + err = c2patool(output) + require.NoError(t, err) + } + }) + } +} + +type C2PAToolOutput struct { + ActiveManifest string `json:"active_manifest"` + Manifests struct { + UrnUUID struct { + ClaimGenerator string `json:"claim_generator"` + ClaimGeneratorInfo []struct { + Name string `json:"name"` + Version string `json:"version"` + } `json:"claim_generator_info"` + Title string `json:"title"` + Format string `json:"format"` + InstanceID string `json:"instance_id"` + Ingredients []any `json:"ingredients"` + Assertions []struct { + Label string `json:"label"` + Data struct { + Actions []struct { + Action string `json:"action"` + } `json:"actions"` + } `json:"data"` + } `json:"assertions"` + SignatureInfo struct { + Alg string `json:"alg"` + Issuer string `json:"issuer"` + CertSerialNumber string `json:"cert_serial_number"` + Time time.Time `json:"time"` + } `json:"signature_info"` + Label string `json:"label"` + } `json:"urn:uuid"` + } `json:"manifests"` + ValidationStatus []struct { + Code string `json:"code"` + URL string `json:"url"` + Explanation string `json:"explanation"` + } `json:"validation_status"` +} + +// validate a file with c2patool +func c2patool(file string) error { + outbs, err := exec.Command("c2patool", file).Output() + if err != nil { + return err + } + var out C2PAToolOutput + err = json.Unmarshal(outbs, &out) + if err != nil { + return err + } + if len(out.ValidationStatus) > 0 { + errbs, err := json.Marshal(out.ValidationStatus) + if err != nil { + panic("validation status testing error") + } + return fmt.Errorf(string(errbs)) + } + return nil +} + +func TestC2PATool(t *testing.T) { + fixtures := getFixtures() + err := c2patool(filepath.Join(fixtures, "C.jpg")) + require.NoError(t, err) + err = c2patool(filepath.Join(fixtures, "screenshot-signed-badsig.jpg")) + require.Error(t, err) +} diff --git a/pkg/c2pa/callbacksigner.go b/pkg/c2pa/callbacksigner.go new file mode 100644 index 0000000..46dc042 --- /dev/null +++ b/pkg/c2pa/callbacksigner.go @@ -0,0 +1,30 @@ +package c2pa + +import ( + "crypto" + "crypto/rand" + + rustC2PA "git.stream.place/streamplace/c2pa-go/pkg/c2pa/generated/c2pa" +) + +type C2PACallbackSigner struct { + signer crypto.Signer + algorithm SigningAlgorithm +} + +func (s *C2PACallbackSigner) Sign(data []byte) ([]byte, *rustC2PA.Error) { + bs, err := s._sign(data) + if err != nil { + return nil, rustC2PA.NewErrorSignature(err.Error()) + } + return bs, nil +} + +func (s *C2PACallbackSigner) _sign(data []byte) ([]byte, error) { + digest, opts, err := s.algorithm.Digest(data) + if err != nil { + return nil, err + } + + return s.signer.Sign(rand.Reader, digest, opts) +} diff --git a/pkg/c2pa/demo/demo.go b/pkg/c2pa/demo/demo.go new file mode 100644 index 0000000..53908c0 --- /dev/null +++ b/pkg/c2pa/demo/demo.go @@ -0,0 +1,115 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + + "git.stream.place/streamplace/c2pa-go/pkg/c2pa" +) + +func Start() error { + fs := flag.NewFlagSet("c2pa-go-demo", flag.ExitOnError) + manifest := fs.String("manifest", "", "manifest file for signing") + cert := fs.String("cert", "", "certificate file to use") + key := fs.String("key", "", "private key file to use") + input := fs.String("input", "", "input file for signing") + output := fs.String("output", "", "output file for signing") + alg := fs.String("alg", "", "algorithm to use to sign (es256, es256k, es384, es512, ps256, ps384, ps512, ed25519)") + pass := os.Args[1:] + err := fs.Parse(pass) + if err != nil { + return err + } + if *manifest != "" || *output != "" { + if *manifest == "" { + return fmt.Errorf("missing --manifest") + } + if *output == "" { + return fmt.Errorf("missing --output") + } + if *input == "" { + return fmt.Errorf("missing --input") + } + if *cert == "" { + return fmt.Errorf("missing --cert") + } + if *key == "" { + return fmt.Errorf("missing --key") + } + if *alg == "" { + return fmt.Errorf("missing --alg") + } + certBytes, err := os.ReadFile(*cert) + if err != nil { + return err + } + keyBytes, err := os.ReadFile(*key) + if err != nil { + return err + } + manifestBytes, err := os.ReadFile(*manifest) + if err != nil { + return err + } + var manifest c2pa.ManifestDefinition + err = json.Unmarshal(manifestBytes, &manifest) + if err != nil { + return err + } + alg, err := c2pa.GetSigningAlgorithm(*alg) + if err != nil { + return err + } + signer, err := c2pa.MakeStaticSigner(keyBytes) + if err != nil { + return err + } + b, err := c2pa.NewBuilder(&manifest, &c2pa.BuilderParams{ + Cert: certBytes, + Signer: signer, + Algorithm: alg, + TAURL: "http://timestamp.digicert.com", + }) + if err != nil { + return err + } + err = b.SignFile(*input, *output) + if err != nil { + return err + } + return nil + } + args := fs.Args() + if len(args) != 1 { + fs.Usage() + fmt.Printf("usage: %s [target-file]\n", os.Args[0]) + return nil + } + fname := args[0] + reader, err := c2pa.FromFile(fname) + if err != nil { + return err + } + + activeManifest := reader.GetActiveManifest() + if activeManifest == nil { + return fmt.Errorf("could not find active manifest") + } + + bs, err := json.MarshalIndent(activeManifest, "", " ") + if err != nil { + return err + } + + fmt.Println(string(bs)) + return nil +} + +func main() { + err := Start() + if err != nil { + panic(err) + } +} diff --git a/pkg/c2pa/generated/c2pa/c2pa.c b/pkg/c2pa/generated/c2pa/c2pa.c new file mode 100644 index 0000000..f9a0d0f --- /dev/null +++ b/pkg/c2pa/generated/c2pa/c2pa.c @@ -0,0 +1,8 @@ +#include + +// This file exists beacause of +// https://github.com/golang/go/issues/11263 + +void cgo_rust_task_callback_bridge_c2pa(RustTaskCallback cb, const void * taskData, int8_t status) { + cb(taskData, status); +} \ No newline at end of file diff --git a/pkg/c2pa/generated/c2pa/c2pa.go b/pkg/c2pa/generated/c2pa/c2pa.go new file mode 100644 index 0000000..e66649e --- /dev/null +++ b/pkg/c2pa/generated/c2pa/c2pa.go @@ -0,0 +1,1996 @@ +package c2pa + +// #include +import "C" + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + "runtime" + "sync" + "sync/atomic" + "unsafe" +) + +type RustBuffer = C.RustBuffer + +type RustBufferI interface { + AsReader() *bytes.Reader + Free() + ToGoBytes() []byte + Data() unsafe.Pointer + Len() int + Capacity() int +} + +func RustBufferFromExternal(b RustBufferI) RustBuffer { + return RustBuffer{ + capacity: C.int(b.Capacity()), + len: C.int(b.Len()), + data: (*C.uchar)(b.Data()), + } +} + +func (cb RustBuffer) Capacity() int { + return int(cb.capacity) +} + +func (cb RustBuffer) Len() int { + return int(cb.len) +} + +func (cb RustBuffer) Data() unsafe.Pointer { + return unsafe.Pointer(cb.data) +} + +func (cb RustBuffer) AsReader() *bytes.Reader { + b := unsafe.Slice((*byte)(cb.data), C.int(cb.len)) + return bytes.NewReader(b) +} + +func (cb RustBuffer) Free() { + rustCall(func(status *C.RustCallStatus) bool { + C.ffi_c2pa_rustbuffer_free(cb, status) + return false + }) +} + +func (cb RustBuffer) ToGoBytes() []byte { + return C.GoBytes(unsafe.Pointer(cb.data), C.int(cb.len)) +} + +func stringToRustBuffer(str string) RustBuffer { + return bytesToRustBuffer([]byte(str)) +} + +func bytesToRustBuffer(b []byte) RustBuffer { + if len(b) == 0 { + return RustBuffer{} + } + // We can pass the pointer along here, as it is pinned + // for the duration of this call + foreign := C.ForeignBytes{ + len: C.int(len(b)), + data: (*C.uchar)(unsafe.Pointer(&b[0])), + } + + return rustCall(func(status *C.RustCallStatus) RustBuffer { + return C.ffi_c2pa_rustbuffer_from_bytes(foreign, status) + }) +} + +type BufLifter[GoType any] interface { + Lift(value RustBufferI) GoType +} + +type BufLowerer[GoType any] interface { + Lower(value GoType) RustBuffer +} + +type FfiConverter[GoType any, FfiType any] interface { + Lift(value FfiType) GoType + Lower(value GoType) FfiType +} + +type BufReader[GoType any] interface { + Read(reader io.Reader) GoType +} + +type BufWriter[GoType any] interface { + Write(writer io.Writer, value GoType) +} + +type FfiRustBufConverter[GoType any, FfiType any] interface { + FfiConverter[GoType, FfiType] + BufReader[GoType] +} + +func LowerIntoRustBuffer[GoType any](bufWriter BufWriter[GoType], value GoType) RustBuffer { + // This might be not the most efficient way but it does not require knowing allocation size + // beforehand + var buffer bytes.Buffer + bufWriter.Write(&buffer, value) + + bytes, err := io.ReadAll(&buffer) + if err != nil { + panic(fmt.Errorf("reading written data: %w", err)) + } + return bytesToRustBuffer(bytes) +} + +func LiftFromRustBuffer[GoType any](bufReader BufReader[GoType], rbuf RustBufferI) GoType { + defer rbuf.Free() + reader := rbuf.AsReader() + item := bufReader.Read(reader) + if reader.Len() > 0 { + // TODO: Remove this + leftover, _ := io.ReadAll(reader) + panic(fmt.Errorf("Junk remaining in buffer after lifting: %s", string(leftover))) + } + return item +} + +func rustCallWithError[U any](converter BufLifter[error], callback func(*C.RustCallStatus) U) (U, error) { + var status C.RustCallStatus + returnValue := callback(&status) + err := checkCallStatus(converter, status) + + return returnValue, err +} + +func checkCallStatus(converter BufLifter[error], status C.RustCallStatus) error { + switch status.code { + case 0: + return nil + case 1: + return converter.Lift(status.errorBuf) + case 2: + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if status.errorBuf.len > 0 { + panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf))) + } else { + panic(fmt.Errorf("Rust panicked while handling Rust panic")) + } + default: + return fmt.Errorf("unknown status code: %d", status.code) + } +} + +func checkCallStatusUnknown(status C.RustCallStatus) error { + switch status.code { + case 0: + return nil + case 1: + panic(fmt.Errorf("function not returning an error returned an error")) + case 2: + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if status.errorBuf.len > 0 { + panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf))) + } else { + panic(fmt.Errorf("Rust panicked while handling Rust panic")) + } + default: + return fmt.Errorf("unknown status code: %d", status.code) + } +} + +func rustCall[U any](callback func(*C.RustCallStatus) U) U { + returnValue, err := rustCallWithError(nil, callback) + if err != nil { + panic(err) + } + return returnValue +} + +func writeInt8(writer io.Writer, value int8) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeUint8(writer io.Writer, value uint8) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeInt16(writer io.Writer, value int16) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeUint16(writer io.Writer, value uint16) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeInt32(writer io.Writer, value int32) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeUint32(writer io.Writer, value uint32) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeInt64(writer io.Writer, value int64) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeUint64(writer io.Writer, value uint64) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeFloat32(writer io.Writer, value float32) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func writeFloat64(writer io.Writer, value float64) { + if err := binary.Write(writer, binary.BigEndian, value); err != nil { + panic(err) + } +} + +func readInt8(reader io.Reader) int8 { + var result int8 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readUint8(reader io.Reader) uint8 { + var result uint8 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readInt16(reader io.Reader) int16 { + var result int16 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readUint16(reader io.Reader) uint16 { + var result uint16 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readInt32(reader io.Reader) int32 { + var result int32 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readUint32(reader io.Reader) uint32 { + var result uint32 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readInt64(reader io.Reader) int64 { + var result int64 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readUint64(reader io.Reader) uint64 { + var result uint64 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readFloat32(reader io.Reader) float32 { + var result float32 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func readFloat64(reader io.Reader) float64 { + var result float64 + if err := binary.Read(reader, binary.BigEndian, &result); err != nil { + panic(err) + } + return result +} + +func init() { + + (&FfiConverterCallbackInterfaceSignerCallback{}).register() + (&FfiConverterCallbackInterfaceStream{}).register() + uniffiCheckChecksums() +} + +func uniffiCheckChecksums() { + // Get the bindings contract version from our ComponentInterface + bindingsContractVersion := 24 + // Get the scaffolding contract version by calling the into the dylib + scaffoldingContractVersion := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint32_t { + return C.ffi_c2pa_uniffi_contract_version(uniffiStatus) + }) + if bindingsContractVersion != int(scaffoldingContractVersion) { + // If this happens try cleaning and rebuilding your project + panic("c2pa: UniFFI contract version mismatch") + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_func_sdk_version(uniffiStatus) + }) + if checksum != 37245 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_func_sdk_version: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_func_version(uniffiStatus) + }) + if checksum != 61576 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_func_version: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_builder_add_ingredient(uniffiStatus) + }) + if checksum != 54967 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_builder_add_ingredient: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_builder_add_resource(uniffiStatus) + }) + if checksum != 12018 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_builder_add_resource: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_builder_from_archive(uniffiStatus) + }) + if checksum != 17341 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_builder_from_archive: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_builder_sign(uniffiStatus) + }) + if checksum != 8729 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_builder_sign: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_builder_to_archive(uniffiStatus) + }) + if checksum != 44718 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_builder_to_archive: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_builder_with_json(uniffiStatus) + }) + if checksum != 29392 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_builder_with_json: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_reader_from_stream(uniffiStatus) + }) + if checksum != 3255 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_reader_from_stream: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_reader_get_provenance_cert_chain(uniffiStatus) + }) + if checksum != 38020 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_reader_get_provenance_cert_chain: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_reader_json(uniffiStatus) + }) + if checksum != 33242 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_reader_json: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_reader_resource_to_stream(uniffiStatus) + }) + if checksum != 44049 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_reader_resource_to_stream: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_constructor_builder_new(uniffiStatus) + }) + if checksum != 8924 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_constructor_builder_new: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_constructor_callbacksigner_new(uniffiStatus) + }) + if checksum != 51503 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_constructor_callbacksigner_new: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_constructor_reader_new(uniffiStatus) + }) + if checksum != 7340 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_constructor_reader_new: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_signercallback_sign(uniffiStatus) + }) + if checksum != 15928 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_signercallback_sign: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_stream_read_stream(uniffiStatus) + }) + if checksum != 4594 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_stream_read_stream: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_stream_seek_stream(uniffiStatus) + }) + if checksum != 32219 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_stream_seek_stream: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_c2pa_checksum_method_stream_write_stream(uniffiStatus) + }) + if checksum != 37641 { + // If this happens try cleaning and rebuilding your project + panic("c2pa: uniffi_c2pa_checksum_method_stream_write_stream: UniFFI API checksum mismatch") + } + } +} + +type FfiConverterUint64 struct{} + +var FfiConverterUint64INSTANCE = FfiConverterUint64{} + +func (FfiConverterUint64) Lower(value uint64) C.uint64_t { + return C.uint64_t(value) +} + +func (FfiConverterUint64) Write(writer io.Writer, value uint64) { + writeUint64(writer, value) +} + +func (FfiConverterUint64) Lift(value C.uint64_t) uint64 { + return uint64(value) +} + +func (FfiConverterUint64) Read(reader io.Reader) uint64 { + return readUint64(reader) +} + +type FfiDestroyerUint64 struct{} + +func (FfiDestroyerUint64) Destroy(_ uint64) {} + +type FfiConverterInt64 struct{} + +var FfiConverterInt64INSTANCE = FfiConverterInt64{} + +func (FfiConverterInt64) Lower(value int64) C.int64_t { + return C.int64_t(value) +} + +func (FfiConverterInt64) Write(writer io.Writer, value int64) { + writeInt64(writer, value) +} + +func (FfiConverterInt64) Lift(value C.int64_t) int64 { + return int64(value) +} + +func (FfiConverterInt64) Read(reader io.Reader) int64 { + return readInt64(reader) +} + +type FfiDestroyerInt64 struct{} + +func (FfiDestroyerInt64) Destroy(_ int64) {} + +type FfiConverterString struct{} + +var FfiConverterStringINSTANCE = FfiConverterString{} + +func (FfiConverterString) Lift(rb RustBufferI) string { + defer rb.Free() + reader := rb.AsReader() + b, err := io.ReadAll(reader) + if err != nil { + panic(fmt.Errorf("reading reader: %w", err)) + } + return string(b) +} + +func (FfiConverterString) Read(reader io.Reader) string { + length := readInt32(reader) + buffer := make([]byte, length) + read_length, err := reader.Read(buffer) + if err != nil { + panic(err) + } + if read_length != int(length) { + panic(fmt.Errorf("bad read length when reading string, expected %d, read %d", length, read_length)) + } + return string(buffer) +} + +func (FfiConverterString) Lower(value string) RustBuffer { + return stringToRustBuffer(value) +} + +func (FfiConverterString) Write(writer io.Writer, value string) { + if len(value) > math.MaxInt32 { + panic("String is too large to fit into Int32") + } + + writeInt32(writer, int32(len(value))) + write_length, err := io.WriteString(writer, value) + if err != nil { + panic(err) + } + if write_length != len(value) { + panic(fmt.Errorf("bad write length when writing string, expected %d, written %d", len(value), write_length)) + } +} + +type FfiDestroyerString struct{} + +func (FfiDestroyerString) Destroy(_ string) {} + +type FfiConverterBytes struct{} + +var FfiConverterBytesINSTANCE = FfiConverterBytes{} + +func (c FfiConverterBytes) Lower(value []byte) RustBuffer { + return LowerIntoRustBuffer[[]byte](c, value) +} + +func (c FfiConverterBytes) Write(writer io.Writer, value []byte) { + if len(value) > math.MaxInt32 { + panic("[]byte is too large to fit into Int32") + } + + writeInt32(writer, int32(len(value))) + write_length, err := writer.Write(value) + if err != nil { + panic(err) + } + if write_length != len(value) { + panic(fmt.Errorf("bad write length when writing []byte, expected %d, written %d", len(value), write_length)) + } +} + +func (c FfiConverterBytes) Lift(rb RustBufferI) []byte { + return LiftFromRustBuffer[[]byte](c, rb) +} + +func (c FfiConverterBytes) Read(reader io.Reader) []byte { + length := readInt32(reader) + buffer := make([]byte, length) + read_length, err := reader.Read(buffer) + if err != nil { + panic(err) + } + if read_length != int(length) { + panic(fmt.Errorf("bad read length when reading []byte, expected %d, read %d", length, read_length)) + } + return buffer +} + +type FfiDestroyerBytes struct{} + +func (FfiDestroyerBytes) Destroy(_ []byte) {} + +// Below is an implementation of synchronization requirements outlined in the link. +// https://github.com/mozilla/uniffi-rs/blob/0dc031132d9493ca812c3af6e7dd60ad2ea95bf0/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt#L31 + +type FfiObject struct { + pointer unsafe.Pointer + callCounter atomic.Int64 + freeFunction func(unsafe.Pointer, *C.RustCallStatus) + destroyed atomic.Bool +} + +func newFfiObject(pointer unsafe.Pointer, freeFunction func(unsafe.Pointer, *C.RustCallStatus)) FfiObject { + return FfiObject{ + pointer: pointer, + freeFunction: freeFunction, + } +} + +func (ffiObject *FfiObject) incrementPointer(debugName string) unsafe.Pointer { + for { + counter := ffiObject.callCounter.Load() + if counter <= -1 { + panic(fmt.Errorf("%v object has already been destroyed", debugName)) + } + if counter == math.MaxInt64 { + panic(fmt.Errorf("%v object call counter would overflow", debugName)) + } + if ffiObject.callCounter.CompareAndSwap(counter, counter+1) { + break + } + } + + return ffiObject.pointer +} + +func (ffiObject *FfiObject) decrementPointer() { + if ffiObject.callCounter.Add(-1) == -1 { + ffiObject.freeRustArcPtr() + } +} + +func (ffiObject *FfiObject) destroy() { + if ffiObject.destroyed.CompareAndSwap(false, true) { + if ffiObject.callCounter.Add(-1) == -1 { + ffiObject.freeRustArcPtr() + } + } +} + +func (ffiObject *FfiObject) freeRustArcPtr() { + rustCall(func(status *C.RustCallStatus) int32 { + ffiObject.freeFunction(ffiObject.pointer, status) + return 0 + }) +} + +type Builder struct { + ffiObject FfiObject +} + +func NewBuilder() *Builder { + return FfiConverterBuilderINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_c2pa_fn_constructor_builder_new(_uniffiStatus) + })) +} + +func (_self *Builder) AddIngredient(ingredientJson string, format string, stream Stream) error { + _pointer := _self.ffiObject.incrementPointer("*Builder") + defer _self.ffiObject.decrementPointer() + _, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) bool { + C.uniffi_c2pa_fn_method_builder_add_ingredient( + _pointer, FfiConverterStringINSTANCE.Lower(ingredientJson), FfiConverterStringINSTANCE.Lower(format), FfiConverterCallbackInterfaceStreamINSTANCE.Lower(stream), _uniffiStatus) + return false + }) + return _uniffiErr +} + +func (_self *Builder) AddResource(uri string, stream Stream) error { + _pointer := _self.ffiObject.incrementPointer("*Builder") + defer _self.ffiObject.decrementPointer() + _, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) bool { + C.uniffi_c2pa_fn_method_builder_add_resource( + _pointer, FfiConverterStringINSTANCE.Lower(uri), FfiConverterCallbackInterfaceStreamINSTANCE.Lower(stream), _uniffiStatus) + return false + }) + return _uniffiErr +} + +func (_self *Builder) FromArchive(stream Stream) error { + _pointer := _self.ffiObject.incrementPointer("*Builder") + defer _self.ffiObject.decrementPointer() + _, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) bool { + C.uniffi_c2pa_fn_method_builder_from_archive( + _pointer, FfiConverterCallbackInterfaceStreamINSTANCE.Lower(stream), _uniffiStatus) + return false + }) + return _uniffiErr +} + +func (_self *Builder) Sign(format string, input Stream, output Stream, signer *CallbackSigner) ([]byte, error) { + _pointer := _self.ffiObject.incrementPointer("*Builder") + defer _self.ffiObject.decrementPointer() + _uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_c2pa_fn_method_builder_sign( + _pointer, FfiConverterStringINSTANCE.Lower(format), FfiConverterCallbackInterfaceStreamINSTANCE.Lower(input), FfiConverterCallbackInterfaceStreamINSTANCE.Lower(output), FfiConverterCallbackSignerINSTANCE.Lower(signer), _uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue []byte + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterBytesINSTANCE.Lift(_uniffiRV), _uniffiErr + } +} + +func (_self *Builder) ToArchive(stream Stream) error { + _pointer := _self.ffiObject.incrementPointer("*Builder") + defer _self.ffiObject.decrementPointer() + _, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) bool { + C.uniffi_c2pa_fn_method_builder_to_archive( + _pointer, FfiConverterCallbackInterfaceStreamINSTANCE.Lower(stream), _uniffiStatus) + return false + }) + return _uniffiErr +} + +func (_self *Builder) WithJson(json string) error { + _pointer := _self.ffiObject.incrementPointer("*Builder") + defer _self.ffiObject.decrementPointer() + _, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) bool { + C.uniffi_c2pa_fn_method_builder_with_json( + _pointer, FfiConverterStringINSTANCE.Lower(json), _uniffiStatus) + return false + }) + return _uniffiErr +} + +func (object *Builder) Destroy() { + runtime.SetFinalizer(object, nil) + object.ffiObject.destroy() +} + +type FfiConverterBuilder struct{} + +var FfiConverterBuilderINSTANCE = FfiConverterBuilder{} + +func (c FfiConverterBuilder) Lift(pointer unsafe.Pointer) *Builder { + result := &Builder{ + newFfiObject( + pointer, + func(pointer unsafe.Pointer, status *C.RustCallStatus) { + C.uniffi_c2pa_fn_free_builder(pointer, status) + }), + } + runtime.SetFinalizer(result, (*Builder).Destroy) + return result +} + +func (c FfiConverterBuilder) Read(reader io.Reader) *Builder { + return c.Lift(unsafe.Pointer(uintptr(readUint64(reader)))) +} + +func (c FfiConverterBuilder) Lower(value *Builder) unsafe.Pointer { + // TODO: this is bad - all synchronization from ObjectRuntime.go is discarded here, + // because the pointer will be decremented immediately after this function returns, + // and someone will be left holding onto a non-locked pointer. + pointer := value.ffiObject.incrementPointer("*Builder") + defer value.ffiObject.decrementPointer() + return pointer +} + +func (c FfiConverterBuilder) Write(writer io.Writer, value *Builder) { + writeUint64(writer, uint64(uintptr(c.Lower(value)))) +} + +type FfiDestroyerBuilder struct{} + +func (_ FfiDestroyerBuilder) Destroy(value *Builder) { + value.Destroy() +} + +type CallbackSigner struct { + ffiObject FfiObject +} + +func NewCallbackSigner(callback SignerCallback, alg SigningAlg, certs []byte, taUrl *string) *CallbackSigner { + return FfiConverterCallbackSignerINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_c2pa_fn_constructor_callbacksigner_new(FfiConverterCallbackInterfaceSignerCallbackINSTANCE.Lower(callback), FfiConverterTypeSigningAlgINSTANCE.Lower(alg), FfiConverterBytesINSTANCE.Lower(certs), FfiConverterOptionalStringINSTANCE.Lower(taUrl), _uniffiStatus) + })) +} + +func (object *CallbackSigner) Destroy() { + runtime.SetFinalizer(object, nil) + object.ffiObject.destroy() +} + +type FfiConverterCallbackSigner struct{} + +var FfiConverterCallbackSignerINSTANCE = FfiConverterCallbackSigner{} + +func (c FfiConverterCallbackSigner) Lift(pointer unsafe.Pointer) *CallbackSigner { + result := &CallbackSigner{ + newFfiObject( + pointer, + func(pointer unsafe.Pointer, status *C.RustCallStatus) { + C.uniffi_c2pa_fn_free_callbacksigner(pointer, status) + }), + } + runtime.SetFinalizer(result, (*CallbackSigner).Destroy) + return result +} + +func (c FfiConverterCallbackSigner) Read(reader io.Reader) *CallbackSigner { + return c.Lift(unsafe.Pointer(uintptr(readUint64(reader)))) +} + +func (c FfiConverterCallbackSigner) Lower(value *CallbackSigner) unsafe.Pointer { + // TODO: this is bad - all synchronization from ObjectRuntime.go is discarded here, + // because the pointer will be decremented immediately after this function returns, + // and someone will be left holding onto a non-locked pointer. + pointer := value.ffiObject.incrementPointer("*CallbackSigner") + defer value.ffiObject.decrementPointer() + return pointer +} + +func (c FfiConverterCallbackSigner) Write(writer io.Writer, value *CallbackSigner) { + writeUint64(writer, uint64(uintptr(c.Lower(value)))) +} + +type FfiDestroyerCallbackSigner struct{} + +func (_ FfiDestroyerCallbackSigner) Destroy(value *CallbackSigner) { + value.Destroy() +} + +type Reader struct { + ffiObject FfiObject +} + +func NewReader() *Reader { + return FfiConverterReaderINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_c2pa_fn_constructor_reader_new(_uniffiStatus) + })) +} + +func (_self *Reader) FromStream(format string, reader Stream) (string, error) { + _pointer := _self.ffiObject.incrementPointer("*Reader") + defer _self.ffiObject.decrementPointer() + _uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_c2pa_fn_method_reader_from_stream( + _pointer, FfiConverterStringINSTANCE.Lower(format), FfiConverterCallbackInterfaceStreamINSTANCE.Lower(reader), _uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue string + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterStringINSTANCE.Lift(_uniffiRV), _uniffiErr + } +} + +func (_self *Reader) GetProvenanceCertChain() (string, error) { + _pointer := _self.ffiObject.incrementPointer("*Reader") + defer _self.ffiObject.decrementPointer() + _uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_c2pa_fn_method_reader_get_provenance_cert_chain( + _pointer, _uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue string + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterStringINSTANCE.Lift(_uniffiRV), _uniffiErr + } +} + +func (_self *Reader) Json() (string, error) { + _pointer := _self.ffiObject.incrementPointer("*Reader") + defer _self.ffiObject.decrementPointer() + _uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_c2pa_fn_method_reader_json( + _pointer, _uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue string + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterStringINSTANCE.Lift(_uniffiRV), _uniffiErr + } +} + +func (_self *Reader) ResourceToStream(uri string, stream Stream) (uint64, error) { + _pointer := _self.ffiObject.incrementPointer("*Reader") + defer _self.ffiObject.decrementPointer() + _uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeError{}, func(_uniffiStatus *C.RustCallStatus) C.uint64_t { + return C.uniffi_c2pa_fn_method_reader_resource_to_stream( + _pointer, FfiConverterStringINSTANCE.Lower(uri), FfiConverterCallbackInterfaceStreamINSTANCE.Lower(stream), _uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue uint64 + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterUint64INSTANCE.Lift(_uniffiRV), _uniffiErr + } +} + +func (object *Reader) Destroy() { + runtime.SetFinalizer(object, nil) + object.ffiObject.destroy() +} + +type FfiConverterReader struct{} + +var FfiConverterReaderINSTANCE = FfiConverterReader{} + +func (c FfiConverterReader) Lift(pointer unsafe.Pointer) *Reader { + result := &Reader{ + newFfiObject( + pointer, + func(pointer unsafe.Pointer, status *C.RustCallStatus) { + C.uniffi_c2pa_fn_free_reader(pointer, status) + }), + } + runtime.SetFinalizer(result, (*Reader).Destroy) + return result +} + +func (c FfiConverterReader) Read(reader io.Reader) *Reader { + return c.Lift(unsafe.Pointer(uintptr(readUint64(reader)))) +} + +func (c FfiConverterReader) Lower(value *Reader) unsafe.Pointer { + // TODO: this is bad - all synchronization from ObjectRuntime.go is discarded here, + // because the pointer will be decremented immediately after this function returns, + // and someone will be left holding onto a non-locked pointer. + pointer := value.ffiObject.incrementPointer("*Reader") + defer value.ffiObject.decrementPointer() + return pointer +} + +func (c FfiConverterReader) Write(writer io.Writer, value *Reader) { + writeUint64(writer, uint64(uintptr(c.Lower(value)))) +} + +type FfiDestroyerReader struct{} + +func (_ FfiDestroyerReader) Destroy(value *Reader) { + value.Destroy() +} + +type Error struct { + err error +} + +func (err Error) Error() string { + return fmt.Sprintf("Error: %s", err.err.Error()) +} + +func (err Error) Unwrap() error { + return err.err +} + +// Err* are used for checking error type with `errors.Is` +var ErrErrorAssertion = fmt.Errorf("ErrorAssertion") +var ErrErrorAssertionNotFound = fmt.Errorf("ErrorAssertionNotFound") +var ErrErrorDecoding = fmt.Errorf("ErrorDecoding") +var ErrErrorEncoding = fmt.Errorf("ErrorEncoding") +var ErrErrorFileNotFound = fmt.Errorf("ErrorFileNotFound") +var ErrErrorIo = fmt.Errorf("ErrorIo") +var ErrErrorJson = fmt.Errorf("ErrorJson") +var ErrErrorManifest = fmt.Errorf("ErrorManifest") +var ErrErrorManifestNotFound = fmt.Errorf("ErrorManifestNotFound") +var ErrErrorNotSupported = fmt.Errorf("ErrorNotSupported") +var ErrErrorOther = fmt.Errorf("ErrorOther") +var ErrErrorRemoteManifest = fmt.Errorf("ErrorRemoteManifest") +var ErrErrorResourceNotFound = fmt.Errorf("ErrorResourceNotFound") +var ErrErrorRwLock = fmt.Errorf("ErrorRwLock") +var ErrErrorSignature = fmt.Errorf("ErrorSignature") +var ErrErrorVerify = fmt.Errorf("ErrorVerify") + +// Variant structs +type ErrorAssertion struct { + Reason string +} + +func NewErrorAssertion( + reason string, +) *Error { + return &Error{ + err: &ErrorAssertion{ + Reason: reason, + }, + } +} + +func (err ErrorAssertion) Error() string { + return fmt.Sprint("Assertion", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorAssertion) Is(target error) bool { + return target == ErrErrorAssertion +} + +type ErrorAssertionNotFound struct { + Reason string +} + +func NewErrorAssertionNotFound( + reason string, +) *Error { + return &Error{ + err: &ErrorAssertionNotFound{ + Reason: reason, + }, + } +} + +func (err ErrorAssertionNotFound) Error() string { + return fmt.Sprint("AssertionNotFound", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorAssertionNotFound) Is(target error) bool { + return target == ErrErrorAssertionNotFound +} + +type ErrorDecoding struct { + Reason string +} + +func NewErrorDecoding( + reason string, +) *Error { + return &Error{ + err: &ErrorDecoding{ + Reason: reason, + }, + } +} + +func (err ErrorDecoding) Error() string { + return fmt.Sprint("Decoding", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorDecoding) Is(target error) bool { + return target == ErrErrorDecoding +} + +type ErrorEncoding struct { + Reason string +} + +func NewErrorEncoding( + reason string, +) *Error { + return &Error{ + err: &ErrorEncoding{ + Reason: reason, + }, + } +} + +func (err ErrorEncoding) Error() string { + return fmt.Sprint("Encoding", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorEncoding) Is(target error) bool { + return target == ErrErrorEncoding +} + +type ErrorFileNotFound struct { + Reason string +} + +func NewErrorFileNotFound( + reason string, +) *Error { + return &Error{ + err: &ErrorFileNotFound{ + Reason: reason, + }, + } +} + +func (err ErrorFileNotFound) Error() string { + return fmt.Sprint("FileNotFound", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorFileNotFound) Is(target error) bool { + return target == ErrErrorFileNotFound +} + +type ErrorIo struct { + Reason string +} + +func NewErrorIo( + reason string, +) *Error { + return &Error{ + err: &ErrorIo{ + Reason: reason, + }, + } +} + +func (err ErrorIo) Error() string { + return fmt.Sprint("Io", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorIo) Is(target error) bool { + return target == ErrErrorIo +} + +type ErrorJson struct { + Reason string +} + +func NewErrorJson( + reason string, +) *Error { + return &Error{ + err: &ErrorJson{ + Reason: reason, + }, + } +} + +func (err ErrorJson) Error() string { + return fmt.Sprint("Json", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorJson) Is(target error) bool { + return target == ErrErrorJson +} + +type ErrorManifest struct { + Reason string +} + +func NewErrorManifest( + reason string, +) *Error { + return &Error{ + err: &ErrorManifest{ + Reason: reason, + }, + } +} + +func (err ErrorManifest) Error() string { + return fmt.Sprint("Manifest", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorManifest) Is(target error) bool { + return target == ErrErrorManifest +} + +type ErrorManifestNotFound struct { + Reason string +} + +func NewErrorManifestNotFound( + reason string, +) *Error { + return &Error{ + err: &ErrorManifestNotFound{ + Reason: reason, + }, + } +} + +func (err ErrorManifestNotFound) Error() string { + return fmt.Sprint("ManifestNotFound", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorManifestNotFound) Is(target error) bool { + return target == ErrErrorManifestNotFound +} + +type ErrorNotSupported struct { + Reason string +} + +func NewErrorNotSupported( + reason string, +) *Error { + return &Error{ + err: &ErrorNotSupported{ + Reason: reason, + }, + } +} + +func (err ErrorNotSupported) Error() string { + return fmt.Sprint("NotSupported", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorNotSupported) Is(target error) bool { + return target == ErrErrorNotSupported +} + +type ErrorOther struct { + Reason string +} + +func NewErrorOther( + reason string, +) *Error { + return &Error{ + err: &ErrorOther{ + Reason: reason, + }, + } +} + +func (err ErrorOther) Error() string { + return fmt.Sprint("Other", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorOther) Is(target error) bool { + return target == ErrErrorOther +} + +type ErrorRemoteManifest struct { + Reason string +} + +func NewErrorRemoteManifest( + reason string, +) *Error { + return &Error{ + err: &ErrorRemoteManifest{ + Reason: reason, + }, + } +} + +func (err ErrorRemoteManifest) Error() string { + return fmt.Sprint("RemoteManifest", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorRemoteManifest) Is(target error) bool { + return target == ErrErrorRemoteManifest +} + +type ErrorResourceNotFound struct { + Reason string +} + +func NewErrorResourceNotFound( + reason string, +) *Error { + return &Error{ + err: &ErrorResourceNotFound{ + Reason: reason, + }, + } +} + +func (err ErrorResourceNotFound) Error() string { + return fmt.Sprint("ResourceNotFound", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorResourceNotFound) Is(target error) bool { + return target == ErrErrorResourceNotFound +} + +type ErrorRwLock struct { +} + +func NewErrorRwLock() *Error { + return &Error{ + err: &ErrorRwLock{}, + } +} + +func (err ErrorRwLock) Error() string { + return fmt.Sprint("RwLock") +} + +func (self ErrorRwLock) Is(target error) bool { + return target == ErrErrorRwLock +} + +type ErrorSignature struct { + Reason string +} + +func NewErrorSignature( + reason string, +) *Error { + return &Error{ + err: &ErrorSignature{ + Reason: reason, + }, + } +} + +func (err ErrorSignature) Error() string { + return fmt.Sprint("Signature", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorSignature) Is(target error) bool { + return target == ErrErrorSignature +} + +type ErrorVerify struct { + Reason string +} + +func NewErrorVerify( + reason string, +) *Error { + return &Error{ + err: &ErrorVerify{ + Reason: reason, + }, + } +} + +func (err ErrorVerify) Error() string { + return fmt.Sprint("Verify", + ": ", + + "Reason=", + err.Reason, + ) +} + +func (self ErrorVerify) Is(target error) bool { + return target == ErrErrorVerify +} + +type FfiConverterTypeError struct{} + +var FfiConverterTypeErrorINSTANCE = FfiConverterTypeError{} + +func (c FfiConverterTypeError) Lift(eb RustBufferI) error { + return LiftFromRustBuffer[error](c, eb) +} + +func (c FfiConverterTypeError) Lower(value *Error) RustBuffer { + return LowerIntoRustBuffer[*Error](c, value) +} + +func (c FfiConverterTypeError) Read(reader io.Reader) error { + errorID := readUint32(reader) + + switch errorID { + case 1: + return &Error{&ErrorAssertion{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 2: + return &Error{&ErrorAssertionNotFound{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 3: + return &Error{&ErrorDecoding{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 4: + return &Error{&ErrorEncoding{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 5: + return &Error{&ErrorFileNotFound{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 6: + return &Error{&ErrorIo{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 7: + return &Error{&ErrorJson{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 8: + return &Error{&ErrorManifest{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 9: + return &Error{&ErrorManifestNotFound{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 10: + return &Error{&ErrorNotSupported{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 11: + return &Error{&ErrorOther{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 12: + return &Error{&ErrorRemoteManifest{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 13: + return &Error{&ErrorResourceNotFound{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 14: + return &Error{&ErrorRwLock{}} + case 15: + return &Error{&ErrorSignature{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + case 16: + return &Error{&ErrorVerify{ + Reason: FfiConverterStringINSTANCE.Read(reader), + }} + default: + panic(fmt.Sprintf("Unknown error code %d in FfiConverterTypeError.Read()", errorID)) + } +} + +func (c FfiConverterTypeError) Write(writer io.Writer, value *Error) { + switch variantValue := value.err.(type) { + case *ErrorAssertion: + writeInt32(writer, 1) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorAssertionNotFound: + writeInt32(writer, 2) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorDecoding: + writeInt32(writer, 3) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorEncoding: + writeInt32(writer, 4) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorFileNotFound: + writeInt32(writer, 5) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorIo: + writeInt32(writer, 6) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorJson: + writeInt32(writer, 7) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorManifest: + writeInt32(writer, 8) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorManifestNotFound: + writeInt32(writer, 9) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorNotSupported: + writeInt32(writer, 10) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorOther: + writeInt32(writer, 11) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorRemoteManifest: + writeInt32(writer, 12) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorResourceNotFound: + writeInt32(writer, 13) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorRwLock: + writeInt32(writer, 14) + case *ErrorSignature: + writeInt32(writer, 15) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + case *ErrorVerify: + writeInt32(writer, 16) + FfiConverterStringINSTANCE.Write(writer, variantValue.Reason) + default: + _ = variantValue + panic(fmt.Sprintf("invalid error value `%v` in FfiConverterTypeError.Write", value)) + } +} + +type SeekMode uint + +const ( + SeekModeStart SeekMode = 1 + SeekModeEnd SeekMode = 2 + SeekModeCurrent SeekMode = 3 +) + +type FfiConverterTypeSeekMode struct{} + +var FfiConverterTypeSeekModeINSTANCE = FfiConverterTypeSeekMode{} + +func (c FfiConverterTypeSeekMode) Lift(rb RustBufferI) SeekMode { + return LiftFromRustBuffer[SeekMode](c, rb) +} + +func (c FfiConverterTypeSeekMode) Lower(value SeekMode) RustBuffer { + return LowerIntoRustBuffer[SeekMode](c, value) +} +func (FfiConverterTypeSeekMode) Read(reader io.Reader) SeekMode { + id := readInt32(reader) + return SeekMode(id) +} + +func (FfiConverterTypeSeekMode) Write(writer io.Writer, value SeekMode) { + writeInt32(writer, int32(value)) +} + +type FfiDestroyerTypeSeekMode struct{} + +func (_ FfiDestroyerTypeSeekMode) Destroy(value SeekMode) { +} + +type SigningAlg uint + +const ( + SigningAlgEs256 SigningAlg = 1 + SigningAlgEs256k SigningAlg = 2 + SigningAlgEs384 SigningAlg = 3 + SigningAlgEs512 SigningAlg = 4 + SigningAlgPs256 SigningAlg = 5 + SigningAlgPs384 SigningAlg = 6 + SigningAlgPs512 SigningAlg = 7 + SigningAlgEd25519 SigningAlg = 8 +) + +type FfiConverterTypeSigningAlg struct{} + +var FfiConverterTypeSigningAlgINSTANCE = FfiConverterTypeSigningAlg{} + +func (c FfiConverterTypeSigningAlg) Lift(rb RustBufferI) SigningAlg { + return LiftFromRustBuffer[SigningAlg](c, rb) +} + +func (c FfiConverterTypeSigningAlg) Lower(value SigningAlg) RustBuffer { + return LowerIntoRustBuffer[SigningAlg](c, value) +} +func (FfiConverterTypeSigningAlg) Read(reader io.Reader) SigningAlg { + id := readInt32(reader) + return SigningAlg(id) +} + +func (FfiConverterTypeSigningAlg) Write(writer io.Writer, value SigningAlg) { + writeInt32(writer, int32(value)) +} + +type FfiDestroyerTypeSigningAlg struct{} + +func (_ FfiDestroyerTypeSigningAlg) Destroy(value SigningAlg) { +} + +type uniffiCallbackResult C.int32_t + +const ( + uniffiIdxCallbackFree uniffiCallbackResult = 0 + uniffiCallbackResultSuccess uniffiCallbackResult = 0 + uniffiCallbackResultError uniffiCallbackResult = 1 + uniffiCallbackUnexpectedResultError uniffiCallbackResult = 2 + uniffiCallbackCancelled uniffiCallbackResult = 3 +) + +type concurrentHandleMap[T any] struct { + leftMap map[uint64]*T + rightMap map[*T]uint64 + currentHandle uint64 + lock sync.RWMutex +} + +func newConcurrentHandleMap[T any]() *concurrentHandleMap[T] { + return &concurrentHandleMap[T]{ + leftMap: map[uint64]*T{}, + rightMap: map[*T]uint64{}, + } +} + +func (cm *concurrentHandleMap[T]) insert(obj *T) uint64 { + cm.lock.Lock() + defer cm.lock.Unlock() + + if existingHandle, ok := cm.rightMap[obj]; ok { + return existingHandle + } + cm.currentHandle = cm.currentHandle + 1 + cm.leftMap[cm.currentHandle] = obj + cm.rightMap[obj] = cm.currentHandle + return cm.currentHandle +} + +func (cm *concurrentHandleMap[T]) remove(handle uint64) bool { + cm.lock.Lock() + defer cm.lock.Unlock() + + if val, ok := cm.leftMap[handle]; ok { + delete(cm.leftMap, handle) + delete(cm.rightMap, val) + } + return false +} + +func (cm *concurrentHandleMap[T]) tryGet(handle uint64) (*T, bool) { + cm.lock.RLock() + defer cm.lock.RUnlock() + + val, ok := cm.leftMap[handle] + return val, ok +} + +type FfiConverterCallbackInterface[CallbackInterface any] struct { + handleMap *concurrentHandleMap[CallbackInterface] +} + +func (c *FfiConverterCallbackInterface[CallbackInterface]) drop(handle uint64) RustBuffer { + c.handleMap.remove(handle) + return RustBuffer{} +} + +func (c *FfiConverterCallbackInterface[CallbackInterface]) Lift(handle uint64) CallbackInterface { + val, ok := c.handleMap.tryGet(handle) + if !ok { + panic(fmt.Errorf("no callback in handle map: %d", handle)) + } + return *val +} + +func (c *FfiConverterCallbackInterface[CallbackInterface]) Read(reader io.Reader) CallbackInterface { + return c.Lift(readUint64(reader)) +} + +func (c *FfiConverterCallbackInterface[CallbackInterface]) Lower(value CallbackInterface) C.uint64_t { + return C.uint64_t(c.handleMap.insert(&value)) +} + +func (c *FfiConverterCallbackInterface[CallbackInterface]) Write(writer io.Writer, value CallbackInterface) { + writeUint64(writer, uint64(c.Lower(value))) +} + +type SignerCallback interface { + Sign(data []byte) ([]byte, *Error) +} + +// foreignCallbackCallbackInterfaceSignerCallback cannot be callable be a compiled function at a same time +type foreignCallbackCallbackInterfaceSignerCallback struct{} + +//export c2pa_cgo_SignerCallback +func c2pa_cgo_SignerCallback(handle C.uint64_t, method C.int32_t, argsPtr *C.uint8_t, argsLen C.int32_t, outBuf *C.RustBuffer) C.int32_t { + cb := FfiConverterCallbackInterfaceSignerCallbackINSTANCE.Lift(uint64(handle)) + switch method { + case 0: + // 0 means Rust is done with the callback, and the callback + // can be dropped by the foreign language. + *outBuf = FfiConverterCallbackInterfaceSignerCallbackINSTANCE.drop(uint64(handle)) + // See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs` + return C.int32_t(uniffiIdxCallbackFree) + + case 1: + var result uniffiCallbackResult + args := unsafe.Slice((*byte)(argsPtr), argsLen) + result = foreignCallbackCallbackInterfaceSignerCallback{}.InvokeSign(cb, args, outBuf) + return C.int32_t(result) + + default: + // This should never happen, because an out of bounds method index won't + // ever be used. Once we can catch errors, we should return an InternalException. + // https://github.com/mozilla/uniffi-rs/issues/351 + return C.int32_t(uniffiCallbackUnexpectedResultError) + } +} + +func (foreignCallbackCallbackInterfaceSignerCallback) InvokeSign(callback SignerCallback, args []byte, outBuf *C.RustBuffer) uniffiCallbackResult { + reader := bytes.NewReader(args) + result, err := callback.Sign(FfiConverterBytesINSTANCE.Read(reader)) + + if err != nil { + // The only way to bypass an unexpected error is to bypass pointer to an empty + // instance of the error + if err.err == nil { + return uniffiCallbackUnexpectedResultError + } + *outBuf = LowerIntoRustBuffer[*Error](FfiConverterTypeErrorINSTANCE, err) + return uniffiCallbackResultError + } + *outBuf = LowerIntoRustBuffer[[]byte](FfiConverterBytesINSTANCE, result) + return uniffiCallbackResultSuccess +} + +type FfiConverterCallbackInterfaceSignerCallback struct { + FfiConverterCallbackInterface[SignerCallback] +} + +var FfiConverterCallbackInterfaceSignerCallbackINSTANCE = &FfiConverterCallbackInterfaceSignerCallback{ + FfiConverterCallbackInterface: FfiConverterCallbackInterface[SignerCallback]{ + handleMap: newConcurrentHandleMap[SignerCallback](), + }, +} + +// This is a static function because only 1 instance is supported for registering +func (c *FfiConverterCallbackInterfaceSignerCallback) register() { + rustCall(func(status *C.RustCallStatus) int32 { + C.uniffi_c2pa_fn_init_callback_signercallback(C.ForeignCallback(C.c2pa_cgo_SignerCallback), status) + return 0 + }) +} + +type FfiDestroyerCallbackInterfaceSignerCallback struct{} + +func (FfiDestroyerCallbackInterfaceSignerCallback) Destroy(value SignerCallback) { +} + +type Stream interface { + ReadStream(length uint64) ([]byte, *Error) + + SeekStream(pos int64, mode SeekMode) (uint64, *Error) + + WriteStream(data []byte) (uint64, *Error) +} + +// foreignCallbackCallbackInterfaceStream cannot be callable be a compiled function at a same time +type foreignCallbackCallbackInterfaceStream struct{} + +//export c2pa_cgo_Stream +func c2pa_cgo_Stream(handle C.uint64_t, method C.int32_t, argsPtr *C.uint8_t, argsLen C.int32_t, outBuf *C.RustBuffer) C.int32_t { + cb := FfiConverterCallbackInterfaceStreamINSTANCE.Lift(uint64(handle)) + switch method { + case 0: + // 0 means Rust is done with the callback, and the callback + // can be dropped by the foreign language. + *outBuf = FfiConverterCallbackInterfaceStreamINSTANCE.drop(uint64(handle)) + // See docs of ForeignCallback in `uniffi/src/ffi/foreigncallbacks.rs` + return C.int32_t(uniffiIdxCallbackFree) + + case 1: + var result uniffiCallbackResult + args := unsafe.Slice((*byte)(argsPtr), argsLen) + result = foreignCallbackCallbackInterfaceStream{}.InvokeReadStream(cb, args, outBuf) + return C.int32_t(result) + case 2: + var result uniffiCallbackResult + args := unsafe.Slice((*byte)(argsPtr), argsLen) + result = foreignCallbackCallbackInterfaceStream{}.InvokeSeekStream(cb, args, outBuf) + return C.int32_t(result) + case 3: + var result uniffiCallbackResult + args := unsafe.Slice((*byte)(argsPtr), argsLen) + result = foreignCallbackCallbackInterfaceStream{}.InvokeWriteStream(cb, args, outBuf) + return C.int32_t(result) + + default: + // This should never happen, because an out of bounds method index won't + // ever be used. Once we can catch errors, we should return an InternalException. + // https://github.com/mozilla/uniffi-rs/issues/351 + return C.int32_t(uniffiCallbackUnexpectedResultError) + } +} + +func (foreignCallbackCallbackInterfaceStream) InvokeReadStream(callback Stream, args []byte, outBuf *C.RustBuffer) uniffiCallbackResult { + reader := bytes.NewReader(args) + result, err := callback.ReadStream(FfiConverterUint64INSTANCE.Read(reader)) + + if err != nil { + // The only way to bypass an unexpected error is to bypass pointer to an empty + // instance of the error + if err.err == nil { + return uniffiCallbackUnexpectedResultError + } + *outBuf = LowerIntoRustBuffer[*Error](FfiConverterTypeErrorINSTANCE, err) + return uniffiCallbackResultError + } + *outBuf = LowerIntoRustBuffer[[]byte](FfiConverterBytesINSTANCE, result) + return uniffiCallbackResultSuccess +} +func (foreignCallbackCallbackInterfaceStream) InvokeSeekStream(callback Stream, args []byte, outBuf *C.RustBuffer) uniffiCallbackResult { + reader := bytes.NewReader(args) + result, err := callback.SeekStream(FfiConverterInt64INSTANCE.Read(reader), FfiConverterTypeSeekModeINSTANCE.Read(reader)) + + if err != nil { + // The only way to bypass an unexpected error is to bypass pointer to an empty + // instance of the error + if err.err == nil { + return uniffiCallbackUnexpectedResultError + } + *outBuf = LowerIntoRustBuffer[*Error](FfiConverterTypeErrorINSTANCE, err) + return uniffiCallbackResultError + } + *outBuf = LowerIntoRustBuffer[uint64](FfiConverterUint64INSTANCE, result) + return uniffiCallbackResultSuccess +} +func (foreignCallbackCallbackInterfaceStream) InvokeWriteStream(callback Stream, args []byte, outBuf *C.RustBuffer) uniffiCallbackResult { + reader := bytes.NewReader(args) + result, err := callback.WriteStream(FfiConverterBytesINSTANCE.Read(reader)) + + if err != nil { + // The only way to bypass an unexpected error is to bypass pointer to an empty + // instance of the error + if err.err == nil { + return uniffiCallbackUnexpectedResultError + } + *outBuf = LowerIntoRustBuffer[*Error](FfiConverterTypeErrorINSTANCE, err) + return uniffiCallbackResultError + } + *outBuf = LowerIntoRustBuffer[uint64](FfiConverterUint64INSTANCE, result) + return uniffiCallbackResultSuccess +} + +type FfiConverterCallbackInterfaceStream struct { + FfiConverterCallbackInterface[Stream] +} + +var FfiConverterCallbackInterfaceStreamINSTANCE = &FfiConverterCallbackInterfaceStream{ + FfiConverterCallbackInterface: FfiConverterCallbackInterface[Stream]{ + handleMap: newConcurrentHandleMap[Stream](), + }, +} + +// This is a static function because only 1 instance is supported for registering +func (c *FfiConverterCallbackInterfaceStream) register() { + rustCall(func(status *C.RustCallStatus) int32 { + C.uniffi_c2pa_fn_init_callback_stream(C.ForeignCallback(C.c2pa_cgo_Stream), status) + return 0 + }) +} + +type FfiDestroyerCallbackInterfaceStream struct{} + +func (FfiDestroyerCallbackInterfaceStream) Destroy(value Stream) { +} + +type FfiConverterOptionalString struct{} + +var FfiConverterOptionalStringINSTANCE = FfiConverterOptionalString{} + +func (c FfiConverterOptionalString) Lift(rb RustBufferI) *string { + return LiftFromRustBuffer[*string](c, rb) +} + +func (_ FfiConverterOptionalString) Read(reader io.Reader) *string { + if readInt8(reader) == 0 { + return nil + } + temp := FfiConverterStringINSTANCE.Read(reader) + return &temp +} + +func (c FfiConverterOptionalString) Lower(value *string) RustBuffer { + return LowerIntoRustBuffer[*string](c, value) +} + +func (_ FfiConverterOptionalString) Write(writer io.Writer, value *string) { + if value == nil { + writeInt8(writer, 0) + } else { + writeInt8(writer, 1) + FfiConverterStringINSTANCE.Write(writer, *value) + } +} + +type FfiDestroyerOptionalString struct{} + +func (_ FfiDestroyerOptionalString) Destroy(value *string) { + if value != nil { + FfiDestroyerString{}.Destroy(*value) + } +} + +func SdkVersion() string { + return FfiConverterStringINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_c2pa_fn_func_sdk_version(_uniffiStatus) + })) +} + +func Version() string { + return FfiConverterStringINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return C.uniffi_c2pa_fn_func_version(_uniffiStatus) + })) +} diff --git a/pkg/c2pa/generated/c2pa/c2pa.h b/pkg/c2pa/generated/c2pa/c2pa.h new file mode 100644 index 0000000..05f4d3b --- /dev/null +++ b/pkg/c2pa/generated/c2pa/c2pa.h @@ -0,0 +1,568 @@ + + +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + + + +#include +#include + +// The following structs are used to implement the lowest level +// of the FFI, and thus useful to multiple uniffied crates. +// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H. +#ifdef UNIFFI_SHARED_H + // We also try to prevent mixing versions of shared uniffi header structs. + // If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V6 + #ifndef UNIFFI_SHARED_HEADER_V6 + #error Combining helper code from multiple versions of uniffi is not supported + #endif // ndef UNIFFI_SHARED_HEADER_V6 +#else +#define UNIFFI_SHARED_H +#define UNIFFI_SHARED_HEADER_V6 +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️ + +typedef struct RustBuffer { + int32_t capacity; + int32_t len; + uint8_t *data; +} RustBuffer; + +typedef int32_t (*ForeignCallback)(uint64_t, int32_t, uint8_t *, int32_t, RustBuffer *); + +// Task defined in Rust that Go executes +typedef void (*RustTaskCallback)(const void *, int8_t); + +// Callback to execute Rust tasks using a Go routine +// +// Args: +// executor: ForeignExecutor lowered into a uint64_t value +// delay: Delay in MS +// task: RustTaskCallback to call +// task_data: data to pass the task callback +typedef int8_t (*ForeignExecutorCallback)(uint64_t, uint32_t, RustTaskCallback, void *); + +typedef struct ForeignBytes { + int32_t len; + const uint8_t *data; +} ForeignBytes; + +// Error definitions +typedef struct RustCallStatus { + int8_t code; + RustBuffer errorBuf; +} RustCallStatus; + +// Continuation callback for UniFFI Futures +typedef void (*RustFutureContinuation)(void * , int8_t); + +// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️ +// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️ +#endif // def UNIFFI_SHARED_H + +// Needed because we can't execute the callback directly from go. +void cgo_rust_task_callback_bridge_c2pa(RustTaskCallback, const void *, int8_t); + +int8_t uniffiForeignExecutorCallbackc2pa(uint64_t, uint32_t, RustTaskCallback, void*); + +void uniffiFutureContinuationCallbackc2pa(void*, int8_t); + +void uniffi_c2pa_fn_free_builder( + void* ptr, + RustCallStatus* out_status +); + +void* uniffi_c2pa_fn_constructor_builder_new( + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_method_builder_add_ingredient( + void* ptr, + RustBuffer ingredient_json, + RustBuffer format, + uint64_t stream, + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_method_builder_add_resource( + void* ptr, + RustBuffer uri, + uint64_t stream, + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_method_builder_from_archive( + void* ptr, + uint64_t stream, + RustCallStatus* out_status +); + +RustBuffer uniffi_c2pa_fn_method_builder_sign( + void* ptr, + RustBuffer format, + uint64_t input, + uint64_t output, + void* signer, + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_method_builder_to_archive( + void* ptr, + uint64_t stream, + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_method_builder_with_json( + void* ptr, + RustBuffer json, + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_free_callbacksigner( + void* ptr, + RustCallStatus* out_status +); + +void* uniffi_c2pa_fn_constructor_callbacksigner_new( + uint64_t callback, + RustBuffer alg, + RustBuffer certs, + RustBuffer ta_url, + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_free_reader( + void* ptr, + RustCallStatus* out_status +); + +void* uniffi_c2pa_fn_constructor_reader_new( + RustCallStatus* out_status +); + +RustBuffer uniffi_c2pa_fn_method_reader_from_stream( + void* ptr, + RustBuffer format, + uint64_t reader, + RustCallStatus* out_status +); + +RustBuffer uniffi_c2pa_fn_method_reader_get_provenance_cert_chain( + void* ptr, + RustCallStatus* out_status +); + +RustBuffer uniffi_c2pa_fn_method_reader_json( + void* ptr, + RustCallStatus* out_status +); + +uint64_t uniffi_c2pa_fn_method_reader_resource_to_stream( + void* ptr, + RustBuffer uri, + uint64_t stream, + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_init_callback_signercallback( + ForeignCallback callback_stub, + RustCallStatus* out_status +); + +void uniffi_c2pa_fn_init_callback_stream( + ForeignCallback callback_stub, + RustCallStatus* out_status +); + +RustBuffer uniffi_c2pa_fn_func_sdk_version( + RustCallStatus* out_status +); + +RustBuffer uniffi_c2pa_fn_func_version( + RustCallStatus* out_status +); + +RustBuffer ffi_c2pa_rustbuffer_alloc( + int32_t size, + RustCallStatus* out_status +); + +RustBuffer ffi_c2pa_rustbuffer_from_bytes( + ForeignBytes bytes, + RustCallStatus* out_status +); + +void ffi_c2pa_rustbuffer_free( + RustBuffer buf, + RustCallStatus* out_status +); + +RustBuffer ffi_c2pa_rustbuffer_reserve( + RustBuffer buf, + int32_t additional, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_continuation_callback_set( + RustFutureContinuation callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_u8( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_u8( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_u8( + void* handle, + RustCallStatus* out_status +); + +uint8_t ffi_c2pa_rust_future_complete_u8( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_i8( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_i8( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_i8( + void* handle, + RustCallStatus* out_status +); + +int8_t ffi_c2pa_rust_future_complete_i8( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_u16( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_u16( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_u16( + void* handle, + RustCallStatus* out_status +); + +uint16_t ffi_c2pa_rust_future_complete_u16( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_i16( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_i16( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_i16( + void* handle, + RustCallStatus* out_status +); + +int16_t ffi_c2pa_rust_future_complete_i16( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_u32( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_u32( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_u32( + void* handle, + RustCallStatus* out_status +); + +uint32_t ffi_c2pa_rust_future_complete_u32( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_i32( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_i32( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_i32( + void* handle, + RustCallStatus* out_status +); + +int32_t ffi_c2pa_rust_future_complete_i32( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_u64( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_u64( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_u64( + void* handle, + RustCallStatus* out_status +); + +uint64_t ffi_c2pa_rust_future_complete_u64( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_i64( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_i64( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_i64( + void* handle, + RustCallStatus* out_status +); + +int64_t ffi_c2pa_rust_future_complete_i64( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_f32( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_f32( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_f32( + void* handle, + RustCallStatus* out_status +); + +float ffi_c2pa_rust_future_complete_f32( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_f64( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_f64( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_f64( + void* handle, + RustCallStatus* out_status +); + +double ffi_c2pa_rust_future_complete_f64( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_pointer( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_pointer( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_pointer( + void* handle, + RustCallStatus* out_status +); + +void* ffi_c2pa_rust_future_complete_pointer( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_rust_buffer( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_rust_buffer( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_rust_buffer( + void* handle, + RustCallStatus* out_status +); + +RustBuffer ffi_c2pa_rust_future_complete_rust_buffer( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_poll_void( + void* handle, + void* uniffi_callback, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_cancel_void( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_free_void( + void* handle, + RustCallStatus* out_status +); + +void ffi_c2pa_rust_future_complete_void( + void* handle, + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_func_sdk_version( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_func_version( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_builder_add_ingredient( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_builder_add_resource( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_builder_from_archive( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_builder_sign( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_builder_to_archive( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_builder_with_json( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_reader_from_stream( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_reader_get_provenance_cert_chain( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_reader_json( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_reader_resource_to_stream( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_constructor_builder_new( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_constructor_callbacksigner_new( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_constructor_reader_new( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_signercallback_sign( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_stream_read_stream( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_stream_seek_stream( + RustCallStatus* out_status +); + +uint16_t uniffi_c2pa_checksum_method_stream_write_stream( + RustCallStatus* out_status +); + +uint32_t ffi_c2pa_uniffi_contract_version( + RustCallStatus* out_status +); + + +int32_t c2pa_cgo_SignerCallback(uint64_t, int32_t, uint8_t *, int32_t, RustBuffer *); +int32_t c2pa_cgo_Stream(uint64_t, int32_t, uint8_t *, int32_t, RustBuffer *); + diff --git a/pkg/c2pa/generated/manifestdefinition/manifestdefinition.go b/pkg/c2pa/generated/manifestdefinition/manifestdefinition.go new file mode 100644 index 0000000..3d1a457 --- /dev/null +++ b/pkg/c2pa/generated/manifestdefinition/manifestdefinition.go @@ -0,0 +1,446 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package manifestdefinition + +// Identifies a person responsible for an action. +type Actor struct { + // List of references to W3C Verifiable Credentials. + Credentials []HashedUri `json:"credentials,omitempty" yaml:"credentials,omitempty" mapstructure:"credentials,omitempty"` + + // An identifier for a human actor, used when the "type" is + // `humanEntry.identified`. + Identifier *string `json:"identifier,omitempty" yaml:"identifier,omitempty" mapstructure:"identifier,omitempty"` +} + +type AssertionData interface{} + +type AssertionDefinition struct { + // Data corresponds to the JSON schema field "data". + Data AssertionDefinitionData `json:"data" yaml:"data" mapstructure:"data"` + + // Label corresponds to the JSON schema field "label". + Label string `json:"label" yaml:"label" mapstructure:"label"` +} + +type AssertionDefinitionData interface{} + +type AssetType struct { + // Type corresponds to the JSON schema field "type". + Type string `json:"type" yaml:"type" mapstructure:"type"` + + // Version corresponds to the JSON schema field "version". + Version *string `json:"version,omitempty" yaml:"version,omitempty" mapstructure:"version,omitempty"` +} + +// Description of the claim generator, or the software used in generating the +// claim. +// +// This structure is also used for actions softwareAgent +type ClaimGeneratorInfo struct { + // hashed URI to the icon (either embedded or remote) + Icon interface{} `json:"icon,omitempty" yaml:"icon,omitempty" mapstructure:"icon,omitempty"` + + // A human readable string naming the claim_generator + Name string `json:"name" yaml:"name" mapstructure:"name"` + + // A human readable string of the product's version + Version *string `json:"version,omitempty" yaml:"version,omitempty" mapstructure:"version,omitempty"` + + AdditionalProperties interface{} +} + +// An x, y coordinate used for specifying vertices in polygons. +type Coordinate struct { + // The coordinate along the x-axis. + X float64 `json:"x" yaml:"x" mapstructure:"x"` + + // The coordinate along the y-axis. + Y float64 `json:"y" yaml:"y" mapstructure:"y"` +} + +// A description of the source for assertion data +type DataSource struct { + // A list of [`Actor`]s associated with this source. + Actors []Actor `json:"actors,omitempty" yaml:"actors,omitempty" mapstructure:"actors,omitempty"` + + // A human-readable string giving details about the source of the assertion data. + Details *string `json:"details,omitempty" yaml:"details,omitempty" mapstructure:"details,omitempty"` + + // A value from among the enumerated list indicating the source of the assertion. + Type string `json:"type" yaml:"type" mapstructure:"type"` +} + +type DateT string + +// A frame range representing starting and ending frames or pages. +// +// If both `start` and `end` are missing, the frame will span the entire asset. +type Frame struct { + // The end of the frame inclusive or the end of the asset if not present. + End *int `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // The start of the frame or the end of the asset if not present. + // + // The first frame/page starts at 0. + Start *int `json:"start,omitempty" yaml:"start,omitempty" mapstructure:"start,omitempty"` +} + +// Hashed Uri structure as defined by C2PA spec It is annotated to produce the +// correctly tagged cbor serialization +type HashedUri struct { + // Alg corresponds to the JSON schema field "alg". + Alg *string `json:"alg,omitempty" yaml:"alg,omitempty" mapstructure:"alg,omitempty"` + + // Hash corresponds to the JSON schema field "hash". + Hash []int `json:"hash" yaml:"hash" mapstructure:"hash"` + + // Url corresponds to the JSON schema field "url". + Url string `json:"url" yaml:"url" mapstructure:"url"` +} + +// An `Ingredient` is any external asset that has been used in the creation of an +// image. +type Ingredient struct { + // The active manifest label (if one exists). + // + // If this ingredient has a [`ManifestStore`], this will hold the label of the + // active [`Manifest`]. + // + // [`Manifest`]: crate::Manifest [`ManifestStore`]: crate::ManifestStore + ActiveManifest *string `json:"active_manifest,omitempty" yaml:"active_manifest,omitempty" mapstructure:"active_manifest,omitempty"` + + // A reference to the actual data of the ingredient. + Data interface{} `json:"data,omitempty" yaml:"data,omitempty" mapstructure:"data,omitempty"` + + // Additional information about the data's type to the ingredient V2 structure. + DataTypes []AssetType `json:"data_types,omitempty" yaml:"data_types,omitempty" mapstructure:"data_types,omitempty"` + + // Additional description of the ingredient. + Description *string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description,omitempty"` + + // Document ID from `xmpMM:DocumentID` in XMP metadata. + DocumentId *string `json:"document_id,omitempty" yaml:"document_id,omitempty" mapstructure:"document_id,omitempty"` + + // The format of the source file as a MIME type. + Format string `json:"format,omitempty" yaml:"format,omitempty" mapstructure:"format,omitempty"` + + // An optional hash of the asset to prevent duplicates. + Hash *string `json:"hash,omitempty" yaml:"hash,omitempty" mapstructure:"hash,omitempty"` + + // URI to an informational page about the ingredient or its data. + InformationalURI *string `json:"informational_URI,omitempty" yaml:"informational_URI,omitempty" mapstructure:"informational_URI,omitempty"` + + // Instance ID from `xmpMM:InstanceID` in XMP metadata. + InstanceId *string `json:"instance_id,omitempty" yaml:"instance_id,omitempty" mapstructure:"instance_id,omitempty"` + + // A [`ManifestStore`] from the source asset extracted as a binary C2PA blob. + // + // [`ManifestStore`]: crate::ManifestStore + ManifestData interface{} `json:"manifest_data,omitempty" yaml:"manifest_data,omitempty" mapstructure:"manifest_data,omitempty"` + + // Any additional [`Metadata`] as defined in the C2PA spec. + // + // [`Manifest`]: crate::Manifest + Metadata interface{} `json:"metadata,omitempty" yaml:"metadata,omitempty" mapstructure:"metadata,omitempty"` + + // URI from `dcterms:provenance` in XMP metadata. + Provenance *string `json:"provenance,omitempty" yaml:"provenance,omitempty" mapstructure:"provenance,omitempty"` + + // Set to `ParentOf` if this is the parent ingredient. + // + // There can only be one parent ingredient in the ingredients. + Relationship interface{} `json:"relationship,omitempty" yaml:"relationship,omitempty" mapstructure:"relationship,omitempty"` + + // Resources corresponds to the JSON schema field "resources". + Resources interface{} `json:"resources,omitempty" yaml:"resources,omitempty" mapstructure:"resources,omitempty"` + + // A thumbnail image capturing the visual state at the time of import. + // + // A tuple of thumbnail MIME format (i.e. `image/jpeg`) and binary bits of the + // image. + Thumbnail interface{} `json:"thumbnail,omitempty" yaml:"thumbnail,omitempty" mapstructure:"thumbnail,omitempty"` + + // A human-readable title, generally source filename. + Title string `json:"title" yaml:"title" mapstructure:"title"` + + // Validation results. + ValidationStatus []ValidationStatus `json:"validation_status,omitempty" yaml:"validation_status,omitempty" mapstructure:"validation_status,omitempty"` +} + +// A Manifest Definition This is used to define a manifest and is used to build a +// ManifestStore A Manifest is a collection of ingredients and assertions It is +// used to define a claim that can be signed and embedded into a file +type ManifestDefinitionSchemaJson struct { + // A list of assertions + Assertions []AssertionDefinition `json:"assertions,omitempty" yaml:"assertions,omitempty" mapstructure:"assertions,omitempty"` + + // Clam Generator Info is always required with at least one entry + ClaimGeneratorInfo []ClaimGeneratorInfo `json:"claim_generator_info,omitempty" yaml:"claim_generator_info,omitempty" mapstructure:"claim_generator_info,omitempty"` + + // The format of the source file as a MIME type. + Format string `json:"format,omitempty" yaml:"format,omitempty" mapstructure:"format,omitempty"` + + // A List of ingredients + Ingredients []Ingredient `json:"ingredients,omitempty" yaml:"ingredients,omitempty" mapstructure:"ingredients,omitempty"` + + // Instance ID from `xmpMM:InstanceID` in XMP metadata. + InstanceId string `json:"instance_id,omitempty" yaml:"instance_id,omitempty" mapstructure:"instance_id,omitempty"` + + // Label corresponds to the JSON schema field "label". + Label *string `json:"label,omitempty" yaml:"label,omitempty" mapstructure:"label,omitempty"` + + // Optional manifest metadata + Metadata []Metadata `json:"metadata,omitempty" yaml:"metadata,omitempty" mapstructure:"metadata,omitempty"` + + // A list of redactions - URIs to a redacted assertions + Redactions []string `json:"redactions,omitempty" yaml:"redactions,omitempty" mapstructure:"redactions,omitempty"` + + // Thumbnail corresponds to the JSON schema field "thumbnail". + Thumbnail interface{} `json:"thumbnail,omitempty" yaml:"thumbnail,omitempty" mapstructure:"thumbnail,omitempty"` + + // A human-readable title, generally source filename. + Title *string `json:"title,omitempty" yaml:"title,omitempty" mapstructure:"title,omitempty"` + + // Optional prefix added to the generated Manifest Label This is typically + // Internet domain name for the vendor (i.e. `adobe`) + Vendor *string `json:"vendor,omitempty" yaml:"vendor,omitempty" mapstructure:"vendor,omitempty"` +} + +// The Metadata structure can be used as part of other assertions or on its own to +// reference others +type Metadata struct { + // DataSource corresponds to the JSON schema field "dataSource". + DataSource interface{} `json:"dataSource,omitempty" yaml:"dataSource,omitempty" mapstructure:"dataSource,omitempty"` + + // DateTime corresponds to the JSON schema field "dateTime". + DateTime interface{} `json:"dateTime,omitempty" yaml:"dateTime,omitempty" mapstructure:"dateTime,omitempty"` + + // Reference corresponds to the JSON schema field "reference". + Reference interface{} `json:"reference,omitempty" yaml:"reference,omitempty" mapstructure:"reference,omitempty"` + + // RegionOfInterest corresponds to the JSON schema field "regionOfInterest". + RegionOfInterest interface{} `json:"regionOfInterest,omitempty" yaml:"regionOfInterest,omitempty" mapstructure:"regionOfInterest,omitempty"` + + // ReviewRatings corresponds to the JSON schema field "reviewRatings". + ReviewRatings []ReviewRating `json:"reviewRatings,omitempty" yaml:"reviewRatings,omitempty" mapstructure:"reviewRatings,omitempty"` + + AdditionalProperties interface{} +} + +// A spatial, temporal, frame, or textual range describing the region of interest. +type Range struct { + // A frame range. + Frame interface{} `json:"frame,omitempty" yaml:"frame,omitempty" mapstructure:"frame,omitempty"` + + // A spatial range. + Shape interface{} `json:"shape,omitempty" yaml:"shape,omitempty" mapstructure:"shape,omitempty"` + + // A textual range. + Text interface{} `json:"text,omitempty" yaml:"text,omitempty" mapstructure:"text,omitempty"` + + // A temporal range. + Time interface{} `json:"time,omitempty" yaml:"time,omitempty" mapstructure:"time,omitempty"` + + // The type of range of interest. + Type interface{} `json:"type" yaml:"type" mapstructure:"type"` +} + +// The type of range for the region of interest. +type RangeType interface{} + +// A region of interest within an asset describing the change. +// +// This struct can be used from +// [`Action::changes`][crate::assertions::Action::changes] or +// [`Metadata::region_of_interest`][crate::assertions::Metadata::region_of_interest]. +type RegionOfInterest struct { + // A free-text string. + Description *string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description,omitempty"` + + // A free-text string representing a machine-readable, unique to this assertion, + // identifier for the region. + Identifier *string `json:"identifier,omitempty" yaml:"identifier,omitempty" mapstructure:"identifier,omitempty"` + + // Additional information about the asset. + Metadata interface{} `json:"metadata,omitempty" yaml:"metadata,omitempty" mapstructure:"metadata,omitempty"` + + // A free-text string representing a human-readable name for the region which + // might be used in a user interface. + Name *string `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` + + // A range describing the region of interest for the specific asset. + Region []Range `json:"region" yaml:"region" mapstructure:"region"` + + // A value from our controlled vocabulary or an entity-specific value (e.g., + // com.litware.coolArea) that represents the role of a region among other regions. + Role interface{} `json:"role,omitempty" yaml:"role,omitempty" mapstructure:"role,omitempty"` + + // A value from a controlled vocabulary such as + // or an entity-specific value + // (e.g., com.litware.newType) that represents the type of thing(s) depicted by a + // region. + // + // Note this field serializes/deserializes into the name `type`. + Type *string `json:"type,omitempty" yaml:"type,omitempty" mapstructure:"type,omitempty"` +} + +type Relationship string + +const RelationshipComponentOf Relationship = "componentOf" +const RelationshipInputTo Relationship = "inputTo" +const RelationshipParentOf Relationship = "parentOf" + +// A reference to a resource to be used in JSON serialization. +// +// The underlying data can be read as a stream via +// [`Reader::resource_to_stream`][crate::Reader::resource_to_stream]. +type ResourceRef struct { + // The algorithm used to hash the resource (if applicable). + Alg *string `json:"alg,omitempty" yaml:"alg,omitempty" mapstructure:"alg,omitempty"` + + // More detailed data types as defined in the C2PA spec. + DataTypes []AssetType `json:"data_types,omitempty" yaml:"data_types,omitempty" mapstructure:"data_types,omitempty"` + + // The mime type of the referenced resource. + Format string `json:"format" yaml:"format" mapstructure:"format"` + + // The hash of the resource (if applicable). + Hash *string `json:"hash,omitempty" yaml:"hash,omitempty" mapstructure:"hash,omitempty"` + + // A URI that identifies the resource as referenced from the manifest. + // + // This may be a JUMBF URI, a file path, a URL or any other string. Relative JUMBF + // URIs will be resolved with the manifest label. Relative file paths will be + // resolved with the base path if provided. + Identifier string `json:"identifier" yaml:"identifier" mapstructure:"identifier"` +} + +// Resource store to contain binary objects referenced from JSON serializable +// structures +type ResourceStore struct { + // Label corresponds to the JSON schema field "label". + Label *string `json:"label,omitempty" yaml:"label,omitempty" mapstructure:"label,omitempty"` + + // Resources corresponds to the JSON schema field "resources". + Resources ResourceStoreResources `json:"resources" yaml:"resources" mapstructure:"resources"` +} + +type ResourceStoreResources map[string][]int + +// A rating on an Assertion. +// +// See +// . +type ReviewRating struct { + // Code corresponds to the JSON schema field "code". + Code *string `json:"code,omitempty" yaml:"code,omitempty" mapstructure:"code,omitempty"` + + // Explanation corresponds to the JSON schema field "explanation". + Explanation string `json:"explanation" yaml:"explanation" mapstructure:"explanation"` + + // Value corresponds to the JSON schema field "value". + Value int `json:"value" yaml:"value" mapstructure:"value"` +} + +// A role describing the region. +type Role interface{} + +// A spatial range representing rectangle, circle, or a polygon. +type Shape struct { + // The height of a rectnagle. + // + // This field can be ignored for circles and polygons. + Height *float64 `json:"height,omitempty" yaml:"height,omitempty" mapstructure:"height,omitempty"` + + // If the range is inside the shape. + // + // The default value is true. + Inside *bool `json:"inside,omitempty" yaml:"inside,omitempty" mapstructure:"inside,omitempty"` + + // THe origin of the coordinate in the shape. + Origin interface{} `json:"origin" yaml:"origin" mapstructure:"origin"` + + // The type of shape. + Type interface{} `json:"type" yaml:"type" mapstructure:"type"` + + // The type of unit for the shape range. + Unit interface{} `json:"unit" yaml:"unit" mapstructure:"unit"` + + // The vertices of the polygon. + // + // This field can be ignored for rectangles and circles. + Vertices []Coordinate `json:"vertices,omitempty" yaml:"vertices,omitempty" mapstructure:"vertices,omitempty"` + + // The width for rectangles or diameter for circles. + // + // This field can be ignored for polygons. + Width *float64 `json:"width,omitempty" yaml:"width,omitempty" mapstructure:"width,omitempty"` +} + +// The type of shape for the range. +type ShapeType interface{} + +// A textual range representing multiple (possibly discontinuous) ranges of text. +type Text struct { + // The ranges of text to select. + Selectors []TextSelectorRange `json:"selectors" yaml:"selectors" mapstructure:"selectors"` +} + +// Selects a range of text via a fragment identifier. +// +// This is modeled after the W3C Web Annotation selector model. +type TextSelector struct { + // The end character offset or the end of the fragment if not present. + End *int `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // Fragment identifier as per RFC3023 (XML) or ISO 32000-2 (PDF), Annex O. + Fragment string `json:"fragment" yaml:"fragment" mapstructure:"fragment"` + + // The start character offset or the start of the fragment if not present. + Start *int `json:"start,omitempty" yaml:"start,omitempty" mapstructure:"start,omitempty"` +} + +// One or two [`TextSelector`][TextSelector] identifiying the range to select. +type TextSelectorRange struct { + // The end of the text range. + End interface{} `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // The start (or entire) text range. + Selector interface{} `json:"selector" yaml:"selector" mapstructure:"selector"` +} + +// A temporal range representing a starting time to an ending time. +type Time struct { + // The end time or the end of the asset if not present. + End *string `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // The start time or the start of the asset if not present. + Start *string `json:"start,omitempty" yaml:"start,omitempty" mapstructure:"start,omitempty"` + + // The type of time. + Type interface{} `json:"type,omitempty" yaml:"type,omitempty" mapstructure:"type,omitempty"` +} + +// The type of time. +type TimeType interface{} + +// The type of unit for the range. +type UnitType interface{} + +type UriOrResource interface{} + +// A `ValidationStatus` struct describes the validation status of a specific part +// of a manifest. +// +// See +// . +type ValidationStatus struct { + // Code corresponds to the JSON schema field "code". + Code string `json:"code" yaml:"code" mapstructure:"code"` + + // Explanation corresponds to the JSON schema field "explanation". + Explanation *string `json:"explanation,omitempty" yaml:"explanation,omitempty" mapstructure:"explanation,omitempty"` + + // Url corresponds to the JSON schema field "url". + Url *string `json:"url,omitempty" yaml:"url,omitempty" mapstructure:"url,omitempty"` +} diff --git a/pkg/c2pa/generated/manifeststore/manifeststore.go b/pkg/c2pa/generated/manifeststore/manifeststore.go new file mode 100644 index 0000000..653252c --- /dev/null +++ b/pkg/c2pa/generated/manifeststore/manifeststore.go @@ -0,0 +1,520 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package manifeststore + +// Identifies a person responsible for an action. +type Actor struct { + // List of references to W3C Verifiable Credentials. + Credentials []HashedUri `json:"credentials,omitempty" yaml:"credentials,omitempty" mapstructure:"credentials,omitempty"` + + // An identifier for a human actor, used when the "type" is + // `humanEntry.identified`. + Identifier *string `json:"identifier,omitempty" yaml:"identifier,omitempty" mapstructure:"identifier,omitempty"` +} + +type AssetType struct { + // Type corresponds to the JSON schema field "type". + Type string `json:"type" yaml:"type" mapstructure:"type"` + + // Version corresponds to the JSON schema field "version". + Version *string `json:"version,omitempty" yaml:"version,omitempty" mapstructure:"version,omitempty"` +} + +// Description of the claim generator, or the software used in generating the +// claim. +// +// This structure is also used for actions softwareAgent +type ClaimGeneratorInfo struct { + // hashed URI to the icon (either embedded or remote) + Icon interface{} `json:"icon,omitempty" yaml:"icon,omitempty" mapstructure:"icon,omitempty"` + + // A human readable string naming the claim_generator + Name string `json:"name" yaml:"name" mapstructure:"name"` + + // A human readable string of the product's version + Version *string `json:"version,omitempty" yaml:"version,omitempty" mapstructure:"version,omitempty"` + + AdditionalProperties interface{} +} + +// An x, y coordinate used for specifying vertices in polygons. +type Coordinate struct { + // The coordinate along the x-axis. + X float64 `json:"x" yaml:"x" mapstructure:"x"` + + // The coordinate along the y-axis. + Y float64 `json:"y" yaml:"y" mapstructure:"y"` +} + +// A description of the source for assertion data +type DataSource struct { + // A list of [`Actor`]s associated with this source. + Actors []Actor `json:"actors,omitempty" yaml:"actors,omitempty" mapstructure:"actors,omitempty"` + + // A human-readable string giving details about the source of the assertion data. + Details *string `json:"details,omitempty" yaml:"details,omitempty" mapstructure:"details,omitempty"` + + // A value from among the enumerated list indicating the source of the assertion. + Type string `json:"type" yaml:"type" mapstructure:"type"` +} + +type DateT string + +// A frame range representing starting and ending frames or pages. +// +// If both `start` and `end` are missing, the frame will span the entire asset. +type Frame struct { + // The end of the frame inclusive or the end of the asset if not present. + End *int `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // The start of the frame or the end of the asset if not present. + // + // The first frame/page starts at 0. + Start *int `json:"start,omitempty" yaml:"start,omitempty" mapstructure:"start,omitempty"` +} + +// Hashed Uri structure as defined by C2PA spec It is annotated to produce the +// correctly tagged cbor serialization +type HashedUri struct { + // Alg corresponds to the JSON schema field "alg". + Alg *string `json:"alg,omitempty" yaml:"alg,omitempty" mapstructure:"alg,omitempty"` + + // Hash corresponds to the JSON schema field "hash". + Hash []int `json:"hash" yaml:"hash" mapstructure:"hash"` + + // Url corresponds to the JSON schema field "url". + Url string `json:"url" yaml:"url" mapstructure:"url"` +} + +// An `Ingredient` is any external asset that has been used in the creation of an +// image. +type Ingredient struct { + // The active manifest label (if one exists). + // + // If this ingredient has a [`ManifestStore`], this will hold the label of the + // active [`Manifest`]. + // + // [`Manifest`]: crate::Manifest [`ManifestStore`]: crate::ManifestStore + ActiveManifest *string `json:"active_manifest,omitempty" yaml:"active_manifest,omitempty" mapstructure:"active_manifest,omitempty"` + + // A reference to the actual data of the ingredient. + Data interface{} `json:"data,omitempty" yaml:"data,omitempty" mapstructure:"data,omitempty"` + + // Additional information about the data's type to the ingredient V2 structure. + DataTypes []AssetType `json:"data_types,omitempty" yaml:"data_types,omitempty" mapstructure:"data_types,omitempty"` + + // Additional description of the ingredient. + Description *string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description,omitempty"` + + // Document ID from `xmpMM:DocumentID` in XMP metadata. + DocumentId *string `json:"document_id,omitempty" yaml:"document_id,omitempty" mapstructure:"document_id,omitempty"` + + // The format of the source file as a MIME type. + Format string `json:"format,omitempty" yaml:"format,omitempty" mapstructure:"format,omitempty"` + + // An optional hash of the asset to prevent duplicates. + Hash *string `json:"hash,omitempty" yaml:"hash,omitempty" mapstructure:"hash,omitempty"` + + // URI to an informational page about the ingredient or its data. + InformationalURI *string `json:"informational_URI,omitempty" yaml:"informational_URI,omitempty" mapstructure:"informational_URI,omitempty"` + + // Instance ID from `xmpMM:InstanceID` in XMP metadata. + InstanceId *string `json:"instance_id,omitempty" yaml:"instance_id,omitempty" mapstructure:"instance_id,omitempty"` + + // A [`ManifestStore`] from the source asset extracted as a binary C2PA blob. + // + // [`ManifestStore`]: crate::ManifestStore + ManifestData interface{} `json:"manifest_data,omitempty" yaml:"manifest_data,omitempty" mapstructure:"manifest_data,omitempty"` + + // Any additional [`Metadata`] as defined in the C2PA spec. + // + // [`Manifest`]: crate::Manifest + Metadata interface{} `json:"metadata,omitempty" yaml:"metadata,omitempty" mapstructure:"metadata,omitempty"` + + // URI from `dcterms:provenance` in XMP metadata. + Provenance *string `json:"provenance,omitempty" yaml:"provenance,omitempty" mapstructure:"provenance,omitempty"` + + // Set to `ParentOf` if this is the parent ingredient. + // + // There can only be one parent ingredient in the ingredients. + Relationship interface{} `json:"relationship,omitempty" yaml:"relationship,omitempty" mapstructure:"relationship,omitempty"` + + // Resources corresponds to the JSON schema field "resources". + Resources interface{} `json:"resources,omitempty" yaml:"resources,omitempty" mapstructure:"resources,omitempty"` + + // A thumbnail image capturing the visual state at the time of import. + // + // A tuple of thumbnail MIME format (i.e. `image/jpeg`) and binary bits of the + // image. + Thumbnail interface{} `json:"thumbnail,omitempty" yaml:"thumbnail,omitempty" mapstructure:"thumbnail,omitempty"` + + // A human-readable title, generally source filename. + Title string `json:"title" yaml:"title" mapstructure:"title"` + + // Validation results. + ValidationStatus []ValidationStatus `json:"validation_status,omitempty" yaml:"validation_status,omitempty" mapstructure:"validation_status,omitempty"` +} + +// A Manifest represents all the information in a c2pa manifest +type Manifest struct { + // A list of assertions + Assertions []ManifestAssertion `json:"assertions,omitempty" yaml:"assertions,omitempty" mapstructure:"assertions,omitempty"` + + // A User Agent formatted string identifying the software/hardware/system produced + // this claim Spaces are not allowed in names, versions can be specified with + // product/1.0 syntax + ClaimGenerator string `json:"claim_generator,omitempty" yaml:"claim_generator,omitempty" mapstructure:"claim_generator,omitempty"` + + // ClaimGeneratorHints corresponds to the JSON schema field + // "claim_generator_hints". + ClaimGeneratorHints ManifestClaimGeneratorHints `json:"claim_generator_hints,omitempty" yaml:"claim_generator_hints,omitempty" mapstructure:"claim_generator_hints,omitempty"` + + // A list of claim generator info data identifying the software/hardware/system + // produced this claim + ClaimGeneratorInfo []ClaimGeneratorInfo `json:"claim_generator_info,omitempty" yaml:"claim_generator_info,omitempty" mapstructure:"claim_generator_info,omitempty"` + + // A List of verified credentials + Credentials []interface{} `json:"credentials,omitempty" yaml:"credentials,omitempty" mapstructure:"credentials,omitempty"` + + // The format of the source file as a MIME type. + Format string `json:"format,omitempty" yaml:"format,omitempty" mapstructure:"format,omitempty"` + + // A List of ingredients + Ingredients []Ingredient `json:"ingredients,omitempty" yaml:"ingredients,omitempty" mapstructure:"ingredients,omitempty"` + + // Instance ID from `xmpMM:InstanceID` in XMP metadata. + InstanceId string `json:"instance_id,omitempty" yaml:"instance_id,omitempty" mapstructure:"instance_id,omitempty"` + + // Label corresponds to the JSON schema field "label". + Label *string `json:"label,omitempty" yaml:"label,omitempty" mapstructure:"label,omitempty"` + + // A list of user metadata for this claim + Metadata []Metadata `json:"metadata,omitempty" yaml:"metadata,omitempty" mapstructure:"metadata,omitempty"` + + // A list of redactions - URIs to a redacted assertions + Redactions []string `json:"redactions,omitempty" yaml:"redactions,omitempty" mapstructure:"redactions,omitempty"` + + // container for binary assets (like thumbnails) + Resources interface{} `json:"resources,omitempty" yaml:"resources,omitempty" mapstructure:"resources,omitempty"` + + // Signature data (only used for reporting) + SignatureInfo interface{} `json:"signature_info,omitempty" yaml:"signature_info,omitempty" mapstructure:"signature_info,omitempty"` + + // Thumbnail corresponds to the JSON schema field "thumbnail". + Thumbnail interface{} `json:"thumbnail,omitempty" yaml:"thumbnail,omitempty" mapstructure:"thumbnail,omitempty"` + + // A human-readable title, generally source filename. + Title *string `json:"title,omitempty" yaml:"title,omitempty" mapstructure:"title,omitempty"` + + // Optional prefix added to the generated Manifest Label This is typically + // Internet domain name for the vendor (i.e. `adobe`) + Vendor *string `json:"vendor,omitempty" yaml:"vendor,omitempty" mapstructure:"vendor,omitempty"` +} + +// A labeled container for an Assertion value in a Manifest +type ManifestAssertion struct { + // The data of the assertion as Value + Data interface{} `json:"data" yaml:"data" mapstructure:"data"` + + // There can be more than one assertion for any label + Instance *int `json:"instance,omitempty" yaml:"instance,omitempty" mapstructure:"instance,omitempty"` + + // The [ManifestAssertionKind] for this assertion (as stored in c2pa content) + Kind interface{} `json:"kind,omitempty" yaml:"kind,omitempty" mapstructure:"kind,omitempty"` + + // An assertion label in reverse domain format + Label string `json:"label" yaml:"label" mapstructure:"label"` +} + +type ManifestAssertionKind string + +const ManifestAssertionKindBinary ManifestAssertionKind = "Binary" +const ManifestAssertionKindCbor ManifestAssertionKind = "Cbor" +const ManifestAssertionKindJson ManifestAssertionKind = "Json" +const ManifestAssertionKindUri ManifestAssertionKind = "Uri" + +type ManifestClaimGeneratorHints map[string]interface{} + +type ManifestData interface{} + +// A Container for a set of Manifests and a ValidationStatus list +type ManifestStoreSchemaJson struct { + // A label for the active (most recent) manifest in the store + ActiveManifest *string `json:"active_manifest,omitempty" yaml:"active_manifest,omitempty" mapstructure:"active_manifest,omitempty"` + + // A HashMap of Manifests + Manifests ManifestStoreSchemaJsonManifests `json:"manifests" yaml:"manifests" mapstructure:"manifests"` + + // ValidationStatus generated when loading the ManifestStore from an asset + ValidationStatus []ValidationStatus `json:"validation_status,omitempty" yaml:"validation_status,omitempty" mapstructure:"validation_status,omitempty"` +} + +// A HashMap of Manifests +type ManifestStoreSchemaJsonManifests map[string]Manifest + +// The Metadata structure can be used as part of other assertions or on its own to +// reference others +type Metadata struct { + // DataSource corresponds to the JSON schema field "dataSource". + DataSource interface{} `json:"dataSource,omitempty" yaml:"dataSource,omitempty" mapstructure:"dataSource,omitempty"` + + // DateTime corresponds to the JSON schema field "dateTime". + DateTime interface{} `json:"dateTime,omitempty" yaml:"dateTime,omitempty" mapstructure:"dateTime,omitempty"` + + // Reference corresponds to the JSON schema field "reference". + Reference interface{} `json:"reference,omitempty" yaml:"reference,omitempty" mapstructure:"reference,omitempty"` + + // RegionOfInterest corresponds to the JSON schema field "regionOfInterest". + RegionOfInterest interface{} `json:"regionOfInterest,omitempty" yaml:"regionOfInterest,omitempty" mapstructure:"regionOfInterest,omitempty"` + + // ReviewRatings corresponds to the JSON schema field "reviewRatings". + ReviewRatings []ReviewRating `json:"reviewRatings,omitempty" yaml:"reviewRatings,omitempty" mapstructure:"reviewRatings,omitempty"` + + AdditionalProperties interface{} +} + +// A spatial, temporal, frame, or textual range describing the region of interest. +type Range struct { + // A frame range. + Frame interface{} `json:"frame,omitempty" yaml:"frame,omitempty" mapstructure:"frame,omitempty"` + + // A spatial range. + Shape interface{} `json:"shape,omitempty" yaml:"shape,omitempty" mapstructure:"shape,omitempty"` + + // A textual range. + Text interface{} `json:"text,omitempty" yaml:"text,omitempty" mapstructure:"text,omitempty"` + + // A temporal range. + Time interface{} `json:"time,omitempty" yaml:"time,omitempty" mapstructure:"time,omitempty"` + + // The type of range of interest. + Type interface{} `json:"type" yaml:"type" mapstructure:"type"` +} + +// The type of range for the region of interest. +type RangeType interface{} + +// A region of interest within an asset describing the change. +// +// This struct can be used from +// [`Action::changes`][crate::assertions::Action::changes] or +// [`Metadata::region_of_interest`][crate::assertions::Metadata::region_of_interest]. +type RegionOfInterest struct { + // A free-text string. + Description *string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description,omitempty"` + + // A free-text string representing a machine-readable, unique to this assertion, + // identifier for the region. + Identifier *string `json:"identifier,omitempty" yaml:"identifier,omitempty" mapstructure:"identifier,omitempty"` + + // Additional information about the asset. + Metadata interface{} `json:"metadata,omitempty" yaml:"metadata,omitempty" mapstructure:"metadata,omitempty"` + + // A free-text string representing a human-readable name for the region which + // might be used in a user interface. + Name *string `json:"name,omitempty" yaml:"name,omitempty" mapstructure:"name,omitempty"` + + // A range describing the region of interest for the specific asset. + Region []Range `json:"region" yaml:"region" mapstructure:"region"` + + // A value from our controlled vocabulary or an entity-specific value (e.g., + // com.litware.coolArea) that represents the role of a region among other regions. + Role interface{} `json:"role,omitempty" yaml:"role,omitempty" mapstructure:"role,omitempty"` + + // A value from a controlled vocabulary such as + // or an entity-specific value + // (e.g., com.litware.newType) that represents the type of thing(s) depicted by a + // region. + // + // Note this field serializes/deserializes into the name `type`. + Type *string `json:"type,omitempty" yaml:"type,omitempty" mapstructure:"type,omitempty"` +} + +type Relationship string + +const RelationshipComponentOf Relationship = "componentOf" +const RelationshipInputTo Relationship = "inputTo" +const RelationshipParentOf Relationship = "parentOf" + +// A reference to a resource to be used in JSON serialization. +// +// The underlying data can be read as a stream via +// [`Reader::resource_to_stream`][crate::Reader::resource_to_stream]. +type ResourceRef struct { + // The algorithm used to hash the resource (if applicable). + Alg *string `json:"alg,omitempty" yaml:"alg,omitempty" mapstructure:"alg,omitempty"` + + // More detailed data types as defined in the C2PA spec. + DataTypes []AssetType `json:"data_types,omitempty" yaml:"data_types,omitempty" mapstructure:"data_types,omitempty"` + + // The mime type of the referenced resource. + Format string `json:"format" yaml:"format" mapstructure:"format"` + + // The hash of the resource (if applicable). + Hash *string `json:"hash,omitempty" yaml:"hash,omitempty" mapstructure:"hash,omitempty"` + + // A URI that identifies the resource as referenced from the manifest. + // + // This may be a JUMBF URI, a file path, a URL or any other string. Relative JUMBF + // URIs will be resolved with the manifest label. Relative file paths will be + // resolved with the base path if provided. + Identifier string `json:"identifier" yaml:"identifier" mapstructure:"identifier"` +} + +// Resource store to contain binary objects referenced from JSON serializable +// structures +type ResourceStore struct { + // Label corresponds to the JSON schema field "label". + Label *string `json:"label,omitempty" yaml:"label,omitempty" mapstructure:"label,omitempty"` + + // Resources corresponds to the JSON schema field "resources". + Resources ResourceStoreResources `json:"resources" yaml:"resources" mapstructure:"resources"` +} + +type ResourceStoreResources map[string][]int + +// A rating on an Assertion. +// +// See +// . +type ReviewRating struct { + // Code corresponds to the JSON schema field "code". + Code *string `json:"code,omitempty" yaml:"code,omitempty" mapstructure:"code,omitempty"` + + // Explanation corresponds to the JSON schema field "explanation". + Explanation string `json:"explanation" yaml:"explanation" mapstructure:"explanation"` + + // Value corresponds to the JSON schema field "value". + Value int `json:"value" yaml:"value" mapstructure:"value"` +} + +// A role describing the region. +type Role interface{} + +// A spatial range representing rectangle, circle, or a polygon. +type Shape struct { + // The height of a rectnagle. + // + // This field can be ignored for circles and polygons. + Height *float64 `json:"height,omitempty" yaml:"height,omitempty" mapstructure:"height,omitempty"` + + // If the range is inside the shape. + // + // The default value is true. + Inside *bool `json:"inside,omitempty" yaml:"inside,omitempty" mapstructure:"inside,omitempty"` + + // THe origin of the coordinate in the shape. + Origin interface{} `json:"origin" yaml:"origin" mapstructure:"origin"` + + // The type of shape. + Type interface{} `json:"type" yaml:"type" mapstructure:"type"` + + // The type of unit for the shape range. + Unit interface{} `json:"unit" yaml:"unit" mapstructure:"unit"` + + // The vertices of the polygon. + // + // This field can be ignored for rectangles and circles. + Vertices []Coordinate `json:"vertices,omitempty" yaml:"vertices,omitempty" mapstructure:"vertices,omitempty"` + + // The width for rectangles or diameter for circles. + // + // This field can be ignored for polygons. + Width *float64 `json:"width,omitempty" yaml:"width,omitempty" mapstructure:"width,omitempty"` +} + +// The type of shape for the range. +type ShapeType interface{} + +// Holds information about a signature +type SignatureInfo struct { + // human readable issuing authority for this signature + Alg interface{} `json:"alg,omitempty" yaml:"alg,omitempty" mapstructure:"alg,omitempty"` + + // The serial number of the certificate + CertSerialNumber *string `json:"cert_serial_number,omitempty" yaml:"cert_serial_number,omitempty" mapstructure:"cert_serial_number,omitempty"` + + // human readable issuing authority for this signature + Issuer *string `json:"issuer,omitempty" yaml:"issuer,omitempty" mapstructure:"issuer,omitempty"` + + // revocation status of the certificate + RevocationStatus *bool `json:"revocation_status,omitempty" yaml:"revocation_status,omitempty" mapstructure:"revocation_status,omitempty"` + + // the time the signature was created + Time *string `json:"time,omitempty" yaml:"time,omitempty" mapstructure:"time,omitempty"` +} + +// Describes the digital signature algorithms allowed by the C2PA spec. +// +// Per +// : +// +// > All digital signatures that are stored in a C2PA Manifest shall > be generated +// using one of the digital signature algorithms and > key types listed as +// described in this section. +type SigningAlg interface{} + +// A textual range representing multiple (possibly discontinuous) ranges of text. +type Text struct { + // The ranges of text to select. + Selectors []TextSelectorRange `json:"selectors" yaml:"selectors" mapstructure:"selectors"` +} + +// Selects a range of text via a fragment identifier. +// +// This is modeled after the W3C Web Annotation selector model. +type TextSelector struct { + // The end character offset or the end of the fragment if not present. + End *int `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // Fragment identifier as per RFC3023 (XML) or ISO 32000-2 (PDF), Annex O. + Fragment string `json:"fragment" yaml:"fragment" mapstructure:"fragment"` + + // The start character offset or the start of the fragment if not present. + Start *int `json:"start,omitempty" yaml:"start,omitempty" mapstructure:"start,omitempty"` +} + +// One or two [`TextSelector`][TextSelector] identifiying the range to select. +type TextSelectorRange struct { + // The end of the text range. + End interface{} `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // The start (or entire) text range. + Selector interface{} `json:"selector" yaml:"selector" mapstructure:"selector"` +} + +// A temporal range representing a starting time to an ending time. +type Time struct { + // The end time or the end of the asset if not present. + End *string `json:"end,omitempty" yaml:"end,omitempty" mapstructure:"end,omitempty"` + + // The start time or the start of the asset if not present. + Start *string `json:"start,omitempty" yaml:"start,omitempty" mapstructure:"start,omitempty"` + + // The type of time. + Type interface{} `json:"type,omitempty" yaml:"type,omitempty" mapstructure:"type,omitempty"` +} + +// The type of time. +type TimeType interface{} + +// The type of unit for the range. +type UnitType interface{} + +type UriOrResource interface{} + +// A `ValidationStatus` struct describes the validation status of a specific part +// of a manifest. +// +// See +// . +type ValidationStatus struct { + // Code corresponds to the JSON schema field "code". + Code string `json:"code" yaml:"code" mapstructure:"code"` + + // Explanation corresponds to the JSON schema field "explanation". + Explanation *string `json:"explanation,omitempty" yaml:"explanation,omitempty" mapstructure:"explanation,omitempty"` + + // Url corresponds to the JSON schema field "url". + Url *string `json:"url,omitempty" yaml:"url,omitempty" mapstructure:"url,omitempty"` +} diff --git a/pkg/c2pa/generated/settings/settings.go b/pkg/c2pa/generated/settings/settings.go new file mode 100644 index 0000000..ad07c6d --- /dev/null +++ b/pkg/c2pa/generated/settings/settings.go @@ -0,0 +1,82 @@ +// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT. + +package settings + +type Builder struct { + // AutoThumbnail corresponds to the JSON schema field "auto_thumbnail". + AutoThumbnail bool `json:"auto_thumbnail" yaml:"auto_thumbnail" mapstructure:"auto_thumbnail"` +} + +type Core struct { + // CompressManifests corresponds to the JSON schema field "compress_manifests". + CompressManifests bool `json:"compress_manifests" yaml:"compress_manifests" mapstructure:"compress_manifests"` + + // Debug corresponds to the JSON schema field "debug". + Debug bool `json:"debug" yaml:"debug" mapstructure:"debug"` + + // HashAlg corresponds to the JSON schema field "hash_alg". + HashAlg string `json:"hash_alg" yaml:"hash_alg" mapstructure:"hash_alg"` + + // MaxMemoryUsage corresponds to the JSON schema field "max_memory_usage". + MaxMemoryUsage *int `json:"max_memory_usage,omitempty" yaml:"max_memory_usage,omitempty" mapstructure:"max_memory_usage,omitempty"` + + // PreferBmffMerkleTree corresponds to the JSON schema field + // "prefer_bmff_merkle_tree". + PreferBmffMerkleTree bool `json:"prefer_bmff_merkle_tree" yaml:"prefer_bmff_merkle_tree" mapstructure:"prefer_bmff_merkle_tree"` + + // PreferBoxHash corresponds to the JSON schema field "prefer_box_hash". + PreferBoxHash bool `json:"prefer_box_hash" yaml:"prefer_box_hash" mapstructure:"prefer_box_hash"` + + // SaltJumbfBoxes corresponds to the JSON schema field "salt_jumbf_boxes". + SaltJumbfBoxes bool `json:"salt_jumbf_boxes" yaml:"salt_jumbf_boxes" mapstructure:"salt_jumbf_boxes"` +} + +type SettingsSchemaJson struct { + // Builder corresponds to the JSON schema field "builder". + Builder Builder `json:"builder" yaml:"builder" mapstructure:"builder"` + + // Core corresponds to the JSON schema field "core". + Core Core `json:"core" yaml:"core" mapstructure:"core"` + + // Trust corresponds to the JSON schema field "trust". + Trust Trust `json:"trust" yaml:"trust" mapstructure:"trust"` + + // Verify corresponds to the JSON schema field "verify". + Verify Verify `json:"verify" yaml:"verify" mapstructure:"verify"` +} + +type Trust struct { + // AllowedList corresponds to the JSON schema field "allowed_list". + AllowedList *string `json:"allowed_list,omitempty" yaml:"allowed_list,omitempty" mapstructure:"allowed_list,omitempty"` + + // PrivateAnchors corresponds to the JSON schema field "private_anchors". + PrivateAnchors *string `json:"private_anchors,omitempty" yaml:"private_anchors,omitempty" mapstructure:"private_anchors,omitempty"` + + // TrustAnchors corresponds to the JSON schema field "trust_anchors". + TrustAnchors *string `json:"trust_anchors,omitempty" yaml:"trust_anchors,omitempty" mapstructure:"trust_anchors,omitempty"` + + // TrustConfig corresponds to the JSON schema field "trust_config". + TrustConfig *string `json:"trust_config,omitempty" yaml:"trust_config,omitempty" mapstructure:"trust_config,omitempty"` +} + +type Verify struct { + // CheckIngredientTrust corresponds to the JSON schema field + // "check_ingredient_trust". + CheckIngredientTrust bool `json:"check_ingredient_trust" yaml:"check_ingredient_trust" mapstructure:"check_ingredient_trust"` + + // OcspFetch corresponds to the JSON schema field "ocsp_fetch". + OcspFetch bool `json:"ocsp_fetch" yaml:"ocsp_fetch" mapstructure:"ocsp_fetch"` + + // RemoteManifestFetch corresponds to the JSON schema field + // "remote_manifest_fetch". + RemoteManifestFetch bool `json:"remote_manifest_fetch" yaml:"remote_manifest_fetch" mapstructure:"remote_manifest_fetch"` + + // VerifyAfterReading corresponds to the JSON schema field "verify_after_reading". + VerifyAfterReading bool `json:"verify_after_reading" yaml:"verify_after_reading" mapstructure:"verify_after_reading"` + + // VerifyAfterSign corresponds to the JSON schema field "verify_after_sign". + VerifyAfterSign bool `json:"verify_after_sign" yaml:"verify_after_sign" mapstructure:"verify_after_sign"` + + // VerifyTrust corresponds to the JSON schema field "verify_trust". + VerifyTrust bool `json:"verify_trust" yaml:"verify_trust" mapstructure:"verify_trust"` +} diff --git a/pkg/c2pa/io.go b/pkg/c2pa/io.go new file mode 100644 index 0000000..bb43602 --- /dev/null +++ b/pkg/c2pa/io.go @@ -0,0 +1,96 @@ +package c2pa + +import ( + "errors" + "fmt" + "io" + + rustC2PA "git.stream.place/streamplace/c2pa-go/pkg/c2pa/generated/c2pa" +) + +// Wrapped io.ReadSeeker for passing to Rust. Doesn't write. +type C2PAStreamReader struct { + io.ReadSeeker +} + +func (s *C2PAStreamReader) ReadStream(length uint64) ([]byte, *rustC2PA.Error) { + return readStream(s.ReadSeeker, length) +} + +func (s *C2PAStreamReader) SeekStream(pos int64, mode rustC2PA.SeekMode) (uint64, *rustC2PA.Error) { + return seekStream(s.ReadSeeker, pos, mode) +} + +func (s *C2PAStreamReader) WriteStream(data []byte) (uint64, *rustC2PA.Error) { + return 0, rustC2PA.NewErrorIo("Writing is not implemented for C2PAStreamReader") +} + +// Wrapped io.Writer for passing to Rust. +type C2PAStreamWriter struct { + io.ReadWriteSeeker +} + +func (s *C2PAStreamWriter) ReadStream(length uint64) ([]byte, *rustC2PA.Error) { + return readStream(s.ReadWriteSeeker, length) +} + +func (s *C2PAStreamWriter) SeekStream(pos int64, mode rustC2PA.SeekMode) (uint64, *rustC2PA.Error) { + return seekStream(s.ReadWriteSeeker, pos, mode) +} + +func (s *C2PAStreamWriter) WriteStream(data []byte) (uint64, *rustC2PA.Error) { + return writeStream(s.ReadWriteSeeker, data) +} + +func readStream(r io.ReadSeeker, length uint64) ([]byte, *rustC2PA.Error) { + // fmt.Printf("read length=%d\n", length) + bs := make([]byte, length) + read, err := r.Read(bs) + if err != nil { + if errors.Is(err, io.EOF) { + if read == 0 { + // fmt.Printf("read EOF read=%d returning empty?", read) + return []byte{}, nil + } + // partial := bs[read:] + // return partial, nil + } + // fmt.Printf("io error=%s\n", err) + return []byte{}, rustC2PA.NewErrorIo(err.Error()) + } + if uint64(read) < length { + partial := bs[:read] + // fmt.Printf("read returning partial read=%d len=%d\n", read, len(partial)) + return partial, nil + } + // fmt.Printf("read returning full read=%d len=%d\n", read, len(bs)) + return bs, nil +} + +func seekStream(r io.ReadSeeker, pos int64, mode rustC2PA.SeekMode) (uint64, *rustC2PA.Error) { + // fmt.Printf("seek pos=%d\n", pos) + var seekMode int + if mode == rustC2PA.SeekModeCurrent { + seekMode = io.SeekCurrent + } else if mode == rustC2PA.SeekModeStart { + seekMode = io.SeekStart + } else if mode == rustC2PA.SeekModeEnd { + seekMode = io.SeekEnd + } else { + // fmt.Printf("seek mode unsupported mode=%d\n", mode) + return 0, rustC2PA.NewErrorNotSupported(fmt.Sprintf("unknown seek mode: %d", mode)) + } + newPos, err := r.Seek(pos, seekMode) + if err != nil { + return 0, rustC2PA.NewErrorIo(err.Error()) + } + return uint64(newPos), nil +} + +func writeStream(w io.ReadWriteSeeker, data []byte) (uint64, *rustC2PA.Error) { + wrote, err := w.Write(data) + if err != nil { + return uint64(wrote), rustC2PA.NewErrorIo(err.Error()) + } + return uint64(wrote), nil +} diff --git a/pkg/c2pa/staticsigner.go b/pkg/c2pa/staticsigner.go new file mode 100644 index 0000000..174d178 --- /dev/null +++ b/pkg/c2pa/staticsigner.go @@ -0,0 +1,106 @@ +package c2pa + +import ( + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "errors" + "fmt" + "reflect" + + "github.com/decred/dcrd/dcrec/secp256k1" +) + +func MakeStaticSigner(keybs []byte) (crypto.Signer, error) { + block, _ := pem.Decode(keybs) + + if block == nil { + return nil, fmt.Errorf("failed to parse PEM block containing the private key") + } + key, err := parsePrivateKey(block.Bytes) + if err != nil { + return nil, fmt.Errorf("parsePrivateKey failed: %s", err.Error()) + } + return key, nil +} + +func parsePrivateKey(der []byte) (crypto.Signer, error) { + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + + if key, err := x509.ParseECPrivateKey(der); err == nil { + return key, nil + } + + key, err := x509.ParsePKCS8PrivateKey(der) + if err == nil { + switch key := key.(type) { + case *rsa.PrivateKey: + return key, nil + case *ecdsa.PrivateKey: + return key, nil + case ed25519.PrivateKey: + return &key, nil + default: + return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping") + } + } + + // Last resort... handle some key types Go doesn't know about. + return parsePKCS8PrivateKey(der) +} + +var OID_RSA_PSS asn1.ObjectIdentifier = []int{1, 2, 840, 113549, 1, 1, 10} +var OID_EC asn1.ObjectIdentifier = []int{1, 2, 840, 10045, 2, 1} +var OID_SECP256K1 asn1.ObjectIdentifier = []int{1, 3, 132, 0, 10} + +func parsePKCS8PrivateKey(der []byte) (crypto.Signer, error) { + var privKey pkcs8 + _, err := asn1.Unmarshal(der, &privKey) + if err != nil { + return nil, fmt.Errorf("asn1.Unmarshal failed: %s", err.Error()) + } + if reflect.DeepEqual(privKey.Algo.Algorithm, OID_RSA_PSS) { + return x509.ParsePKCS1PrivateKey(privKey.PrivateKey) + } else if reflect.DeepEqual(privKey.Algo.Algorithm, OID_EC) { + return parseES256KPrivateKey(privKey) + } else { + return nil, fmt.Errorf("unknown pkcs8 OID: %s", privKey.Algo.Algorithm) + } +} + +func parseES256KPrivateKey(privKey pkcs8) (crypto.Signer, error) { + var namedCurveOID asn1.ObjectIdentifier + if _, err := asn1.Unmarshal(privKey.Algo.Parameters.FullBytes, &namedCurveOID); err != nil { + return nil, fmt.Errorf("asn1.Unmarshal for oid failed: %w", err) + } + if !reflect.DeepEqual(namedCurveOID, OID_SECP256K1) { + return nil, fmt.Errorf("unknown named curve OID: %s", namedCurveOID.String()) + } + var curveKey ecPrivateKey + _, err := asn1.Unmarshal(privKey.PrivateKey, &curveKey) + if err != nil { + return nil, fmt.Errorf("asn1.Unmarshal for private key failed: %w", err) + } + key, _ := secp256k1.PrivKeyFromBytes(curveKey.PrivateKey) + return key.ToECDSA(), nil +} + +type pkcs8 struct { + Version int + Algo pkix.AlgorithmIdentifier + PrivateKey []byte +} + +type ecPrivateKey struct { + Version int + PrivateKey []byte + NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"` + PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"` +} diff --git a/src/c2pa.udl b/src/c2pa.udl index 9bb1989..857d671 100644 --- a/src/c2pa.udl +++ b/src/c2pa.udl @@ -26,6 +26,7 @@ interface Error { enum SigningAlg { "Es256", + "Es256k", "Es384", "Es512", "Ps256", @@ -60,6 +61,9 @@ interface Reader { [Throws=Error] string json(); + [Throws=Error] + string get_provenance_cert_chain(); + [Throws=Error] u64 resource_to_stream([ByRef] string uri, [ByRef] Stream stream); }; @@ -75,7 +79,7 @@ interface CallbackSigner { interface Builder { constructor(); - + [Throws=Error] void with_json([ByRef] string json); diff --git a/src/lib.rs b/src/lib.rs index 23b0d52..a903eaf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,6 +85,14 @@ impl Reader { } } + pub fn get_provenance_cert_chain(&self) -> Result { + if let Ok(st) = self.reader.try_read() { + Ok(st.get_provenance_cert_chain()?) + } else { + Err(Error::RwLock) + } + } + pub fn resource_to_stream(&self, uri: &str, stream: &dyn Stream) -> Result { if let Ok(reader) = self.reader.try_read() { let mut stream = StreamAdapter::from(stream); diff --git a/src/streams.rs b/src/streams.rs index 960c9b4..62001e0 100644 --- a/src/streams.rs +++ b/src/streams.rs @@ -51,6 +51,10 @@ impl Stream for Box { } } +unsafe impl uniffi::LiftRef for Box { + type LiftType = Box; +} + impl AsMut for dyn Stream { fn as_mut(&mut self) -> &mut Self { self diff --git a/tests/fixtures/ed25519_certs.pem b/tests/fixtures/ed25519_certs.pem new file mode 100644 index 0000000..1a70d08 --- /dev/null +++ b/tests/fixtures/ed25519_certs.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIICSDCCAfqgAwIBAgIUDXaXYTVWskbzUnZ+o/7/DYqA0EEwBQYDK2VwMIGMMQsw +CQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEnMCUG +A1UECgweQzJQQSBUZXN0IEludGVybWVkaWF0ZSBSb290IENBMRkwFwYDVQQLDBBG +T1IgVEVTVElOR19PTkxZMRgwFgYDVQQDDA9JbnRlcm1lZGlhdGUgQ0EwHhcNMjQw +ODExMjMzNDU2WhcNMzQwODA5MjMzNDU2WjCBgDELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUxHzAdBgNVBAoMFkMyUEEgVGVzdCBT +aWduaW5nIENlcnQxGTAXBgNVBAsMEEZPUiBURVNUSU5HX09OTFkxFDASBgNVBAMM +C0MyUEEgU2lnbmVyMCowBQYDK2VwAyEAZucl5DLFWQ4PDLdC763u76cAaFqglztY +Xg+BpbtBaQijeDB2MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH +AwQwDgYDVR0PAQH/BAQDAgbAMB0GA1UdDgQWBBSUvfJfsKISTaP2fbbFgfByEmtX +1zAfBgNVHSMEGDAWgBQyr6rA6G0yWduqQpnurZxS0u5LdzAFBgMrZXADQQCGsyg7 +Cq1/8JmeWVdC+Mxgloalrfu3qkU/2OwKD8YLnQQyE2ipzzfy0+Ib5oVm6pwvSdlL +GIkcdoyiMi5ynoYL +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICKTCCAdugAwIBAgIUOv0/dMK+3bRC1sUFH6NZAbdamqQwBQYDK2VwMHcxCzAJ +BgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJlMRowGAYD +VQQKDBFDMlBBIFRlc3QgUm9vdCBDQTEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05M +WTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yNDA4MTEyMzM0NTZaFw0zNDA4MDkyMzM0 +NTZaMIGMMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3 +aGVyZTEnMCUGA1UECgweQzJQQSBUZXN0IEludGVybWVkaWF0ZSBSb290IENBMRkw +FwYDVQQLDBBGT1IgVEVTVElOR19PTkxZMRgwFgYDVQQDDA9JbnRlcm1lZGlhdGUg +Q0EwKjAFBgMrZXADIQCdb2IH9kGaMPM/oM0Gv7Eee601SUN6N8ibjxrrSVR0w6Nj +MGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFDKv +qsDobTJZ26pCme6tnFLS7kt3MB8GA1UdIwQYMBaAFEAEgXoUd3ybEHj+imNsl/sQ +pL3vMAUGAytlcANBAOWQOwGlJuyUl6psze8FpwR11VZb82pHD8IHUVpPyUKhd+2m +KeSlhwk54EM8yk86NKuWU2FKuqjQSJh8fiyPSAs= +-----END CERTIFICATE----- diff --git a/tests/fixtures/ed25519_private.key b/tests/fixtures/ed25519_private.key new file mode 100644 index 0000000..6ea3596 --- /dev/null +++ b/tests/fixtures/ed25519_private.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEICmROM+3UVzoh4m0NfqGHNkht3AVgcFMf7uVWb+Ih7It +-----END PRIVATE KEY----- diff --git a/tests/fixtures/es256_certs.pem b/tests/fixtures/es256_certs.pem index f24c51c..db564a7 100644 --- a/tests/fixtures/es256_certs.pem +++ b/tests/fixtures/es256_certs.pem @@ -1,32 +1,31 @@ -----BEGIN CERTIFICATE----- -MIIChzCCAi6gAwIBAgIUcCTmJHYF8dZfG0d1UdT6/LXtkeYwCgYIKoZIzj0EAwIw +MIICiTCCAi6gAwIBAgIUEXLS6GFPdb9uvjrP441Zp/Sj70EwCgYIKoZIzj0EAwIw gYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJl MScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3QgQ0ExGTAXBgNV BAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAe -Fw0yMjA2MTAxODQ2NDBaFw0zMDA4MjYxODQ2NDBaMIGAMQswCQYDVQQGEwJVUzEL +Fw0yNDA4MTEyMzM0NTZaFw0zNDA4MDkyMzM0NTZaMIGAMQswCQYDVQQGEwJVUzEL MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEfMB0GA1UECgwWQzJQQSBU ZXN0IFNpZ25pbmcgQ2VydDEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05MWTEUMBIG -A1UEAwwLQzJQQSBTaWduZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQPaL6R -kAkYkKU4+IryBSYxJM3h77sFiMrbvbI8fG7w2Bbl9otNG/cch3DAw5rGAPV7NWky -l3QGuV/wt0MrAPDoo3gwdjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG -AQUFBwMEMA4GA1UdDwEB/wQEAwIGwDAdBgNVHQ4EFgQUFznP0y83joiNOCedQkxT -tAMyNcowHwYDVR0jBBgwFoAUDnyNcma/osnlAJTvtW6A4rYOL2swCgYIKoZIzj0E -AwIDRwAwRAIgOY/2szXjslg/MyJFZ2y7OH8giPYTsvS7UPRP9GI9NgICIDQPMKrE -LQUJEtipZ0TqvI/4mieoyRCeIiQtyuS0LACz +A1UEAwwLQzJQQSBTaWduZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATNcBHD +JH3h1HeAJ/cEkRGe+uYpqRgW8dpKt0NSCOmj3FjhiULvfksqI+hcf94Uf+WDj+Fe +tuUqDHs1qjr6Ep+Zo3gwdjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG +AQUFBwMEMA4GA1UdDwEB/wQEAwIGwDAdBgNVHQ4EFgQUQWf4eoYn/Ps/XOR7EKK6 +P1+tF5MwHwYDVR0jBBgwFoAULee8f++nkLoO+miS9Ma9xmH9b/gwCgYIKoZIzj0E +AwIDSQAwRgIhAL5cwp7d2g6EkCyL0iV/BtAD/bMTyTih8XYWCrcG5XfGAiEAoIKc +Vk7cDoZUKmmPKkCduY882sHpq+i4aOWeyD/Rpg0= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIICajCCAg+gAwIBAgIUfXDXHH+6GtA2QEBX2IvJ2YnGMnUwCgYIKoZIzj0EAwIw +MIICaDCCAg+gAwIBAgIUEDZr9NXIjPfbs7c6qQfuduaK9wYwCgYIKoZIzj0EAwIw dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO -R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTIyMDYxMDE4NDY0MFoXDTMwMDgy -NzE4NDY0MFowgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJ +R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTI0MDgxMTIzMzQ1NloXDTM0MDgw +OTIzMzQ1NlowgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJ U29tZXdoZXJlMScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3Qg Q0ExGTAXBgNVBAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVk -aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHllI4O7a0EkpTYAWfPM -D6Rnfk9iqhEmCQKMOR6J47Rvh2GGjUw4CS+aLT89ySukPTnzGsMQ4jK9d3V4Aq4Q -LsOjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW -BBQOfI1yZr+iyeUAlO+1boDitg4vazAfBgNVHSMEGDAWgBRembiG4Xgb2VcVWnUA -UrYpDsuojDAKBggqhkjOPQQDAgNJADBGAiEAtdZ3+05CzFo90fWeZ4woeJcNQC4B -84Ill3YeZVvR8ZECIQDVRdha1xEDKuNTAManY0zthSosfXcvLnZui1A/y/DYeg== +aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAbMGkgqDWz48DABkqIj +k7JzHwtmILCc2oXBD/ua09U3QDc2LlWTtx5o1D2/07M/CGB+QSQ9mnSrXNfIW/UG +C+WjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBQt57x/76eQug76aJL0xr3GYf1v+DAfBgNVHSMEGDAWgBSFn9J0fZVahqueaXWL +VR2ZF/FliDAKBggqhkjOPQQDAgNHADBEAiB6L2vZwCt8DdW9KJB3TNd8IZt4D7uC +H4+SLDkyyix/KAIgIUdz94JDfp6YCToagYXelMntPcRmwObgW1u31Cw+a64= -----END CERTIFICATE----- - diff --git a/tests/fixtures/es256_private.key b/tests/fixtures/es256_private.key index 5e59fcc..1e494ac 100644 --- a/tests/fixtures/es256_private.key +++ b/tests/fixtures/es256_private.key @@ -1,5 +1,5 @@ -----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgfNJBsaRLSeHizv0m -GL+gcn78QmtfLSm+n+qG9veC2W2hRANCAAQPaL6RkAkYkKU4+IryBSYxJM3h77sF -iMrbvbI8fG7w2Bbl9otNG/cch3DAw5rGAPV7NWkyl3QGuV/wt0MrAPDo +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgaQIFUE7T2LjT4v5K +fjZRBkVJt6AzwD+EuWxi4CEcfayhRANCAATNcBHDJH3h1HeAJ/cEkRGe+uYpqRgW +8dpKt0NSCOmj3FjhiULvfksqI+hcf94Uf+WDj+FetuUqDHs1qjr6Ep+Z -----END PRIVATE KEY----- diff --git a/tests/fixtures/es256k_certs.pem b/tests/fixtures/es256k_certs.pem new file mode 100644 index 0000000..2528b27 --- /dev/null +++ b/tests/fixtures/es256k_certs.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIChDCCAiugAwIBAgIUBW/ByXEeQ0Qpgc6G1HYKjM2j6JcwCgYIKoZIzj0EAwIw +gYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJl +MScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3QgQ0ExGTAXBgNV +BAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAe +Fw0yNDA4MTEyMzM0NTZaFw0zNDA4MDkyMzM0NTZaMIGAMQswCQYDVQQGEwJVUzEL +MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEfMB0GA1UECgwWQzJQQSBU +ZXN0IFNpZ25pbmcgQ2VydDEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05MWTEUMBIG +A1UEAwwLQzJQQSBTaWduZXIwVjAQBgcqhkjOPQIBBgUrgQQACgNCAAR1RJfnhmsE +HUATmWV+p0fuOPl+G0TwZ5ZisGwWFA/J+fD6wjP6mW44Ob3TTMLMCCFfy5Gl5Cju +XJru19UH0wVLo3gwdjAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF +BwMEMA4GA1UdDwEB/wQEAwIGwDAdBgNVHQ4EFgQUoEZwqyiVTYCOTjxn9MeCBDvk +hecwHwYDVR0jBBgwFoAUP9auno3ORuwY1JnRQHu3RCiWgi0wCgYIKoZIzj0EAwID +RwAwRAIgaOz0GFjrKWJMs2epuDqUOis7MsH0ivrPfonvwapYpfYCIBqOURwT+pYf +W0VshLAxI/iVw/5eVXtDPZzCX0b0xq3f +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICZTCCAgygAwIBAgIUIiJUPMeqKEyhrHFdKsVYF6STAqAwCgYIKoZIzj0EAwIw +dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx +GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO +R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTI0MDgxMTIzMzQ1NloXDTM0MDgw +OTIzMzQ1NlowgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJ +U29tZXdoZXJlMScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3Qg +Q0ExGTAXBgNVBAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVk +aWF0ZSBDQTBWMBAGByqGSM49AgEGBSuBBAAKA0IABMi5X2ELOtZ2i19DplQKEgAf +Et6eCXpF+s4M57ak7Rd+1LzpQ+hlRXzvrpW6hLiO+ZaRTmQyqozgWwOBUm52rT2j +YzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBQ/ +1q6ejc5G7BjUmdFAe7dEKJaCLTAfBgNVHSMEGDAWgBSloXNM8yfsV/w3xH7H3pfj +cfWj6jAKBggqhkjOPQQDAgNHADBEAiBievQIsuEy1I3p5XNtpHZ3MBifukoYwo/a +4ZXq8/VK7wIgMseui+Y0BFyDd+d3vd5Jy4d3uhpho6aNFln0qHbhFr8= +-----END CERTIFICATE----- diff --git a/tests/fixtures/es256k_private.key b/tests/fixtures/es256k_private.key new file mode 100644 index 0000000..ece41fb --- /dev/null +++ b/tests/fixtures/es256k_private.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgKJyB05ZmsgeVQ/291hKX +mLsopnxVDVAEYoL1vL1jglahRANCAAR1RJfnhmsEHUATmWV+p0fuOPl+G0TwZ5Zi +sGwWFA/J+fD6wjP6mW44Ob3TTMLMCCFfy5Gl5CjuXJru19UH0wVL +-----END PRIVATE KEY----- diff --git a/tests/fixtures/es384_certs.pem b/tests/fixtures/es384_certs.pem new file mode 100644 index 0000000..da635ed --- /dev/null +++ b/tests/fixtures/es384_certs.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIICxjCCAkugAwIBAgIUPxwNvZFavwPMmdn34oVn4EPQFQkwCgYIKoZIzj0EAwMw +gYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJl +MScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3QgQ0ExGTAXBgNV +BAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAe +Fw0yNDA4MTEyMzM0NTZaFw0zNDA4MDkyMzM0NTZaMIGAMQswCQYDVQQGEwJVUzEL +MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEfMB0GA1UECgwWQzJQQSBU +ZXN0IFNpZ25pbmcgQ2VydDEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05MWTEUMBIG +A1UEAwwLQzJQQSBTaWduZXIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQ9GioplQEj +U21/IpnfY0uDH4uRyX2t8DMtbdSBHUlwP+OxBXGeP0iO21dGBspJnDIbOPy3sOsm +aXezhDICQwY0OHu2Snb1vra3oRgWRfVkgymdawpjWTAG2UEYImjNgh6jeDB2MAwG +A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwQwDgYDVR0PAQH/BAQD +AgbAMB0GA1UdDgQWBBSVO7p+niNfGBeP9qzP1VI8Kw8vTDAfBgNVHSMEGDAWgBQM +viB42FsJTjGfC1IHfhxObxbmqzAKBggqhkjOPQQDAwNpADBmAjEA5CKls6B5cBba +O5CMA4pAUqvEq1lAOBsuJb7asOwvcOxsIv09XaILHDVi4uHpNuOvAjEAxfD6u/Vk +B4Z54ypIZmEwrllIoJ17ivFYrFefuPiRKObbuDhWdwcAGPKKSFkUjKG/ +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICpjCCAiygAwIBAgIUYqrRkwy5rIhBX4LsAcs0B0aZtvQwCgYIKoZIzj0EAwMw +dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx +GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO +R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTI0MDgxMTIzMzQ1NloXDTM0MDgw +OTIzMzQ1NlowgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJ +U29tZXdoZXJlMScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3Qg +Q0ExGTAXBgNVBAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVk +aWF0ZSBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABFQGuuszAH0UsUP56wf71McK +24RIJ9DtDFwNBRAdtnszrYyte/F/xvW3iytV0T+pciZwOsiSZIshakGIHAG8xE45 +S70sQ7Q8y9c59hhDXbEWIJv6t5PQox5FmtASIjqH1qNjMGEwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFAy+IHjYWwlOMZ8LUgd+HE5v +FuarMB8GA1UdIwQYMBaAFAdr3i8zFPjnNPfQlrwlzz8g+/qtMAoGCCqGSM49BAMD +A2gAMGUCMQCvXtMCNmDGM4qL4c45IQYVBKfoq59SsTH8sUWErwcYaUKEiHly6MsT +U91DTNcWNgUCMGULrPzvqATaFwL/KlZ91BAyA8uDj6vIOkxSQTPTlQzP1fmAdL5M +7oIwnU3Ory5PAQ== +-----END CERTIFICATE----- diff --git a/tests/fixtures/es384_private.key b/tests/fixtures/es384_private.key new file mode 100644 index 0000000..17a029c --- /dev/null +++ b/tests/fixtures/es384_private.key @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCnGeB0R890A18yQrE6 +cTWHV7/IpJ9docV6KYR8PiLPN+0FoOuP2fJBNd02jfoLGHWhZANiAAQ9GioplQEj +U21/IpnfY0uDH4uRyX2t8DMtbdSBHUlwP+OxBXGeP0iO21dGBspJnDIbOPy3sOsm +aXezhDICQwY0OHu2Snb1vra3oRgWRfVkgymdawpjWTAG2UEYImjNgh4= +-----END PRIVATE KEY----- diff --git a/tests/fixtures/es512_certs.pem b/tests/fixtures/es512_certs.pem new file mode 100644 index 0000000..dfcaeb0 --- /dev/null +++ b/tests/fixtures/es512_certs.pem @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIDEDCCAnGgAwIBAgIURCnrZVKnXUhFXYLNW+j8euoU+YIwCgYIKoZIzj0EAwIw +gYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU29tZXdoZXJl +MScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3QgQ0ExGTAXBgNV +BAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAe +Fw0yNDA4MTEyMzM0NTZaFw0zNDA4MDkyMzM0NTZaMIGAMQswCQYDVQQGEwJVUzEL +MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNvbWV3aGVyZTEfMB0GA1UECgwWQzJQQSBU +ZXN0IFNpZ25pbmcgQ2VydDEZMBcGA1UECwwQRk9SIFRFU1RJTkdfT05MWTEUMBIG +A1UEAwwLQzJQQSBTaWduZXIwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAAptbys +TzJHzOX9BAYOjkTXS5vVJA3ruVv1JkDot6Z3GMSTwdXVulcyNGlW3qjPTMASyTew +GB2rek0ARv6nnshx2wGnLu8YAWL3JRmMFoOoR7e7FOEz6OOOCeMNR9yyUCYbMMxT +LQo8CN/aOr+FPlldN+ol6J83eL5LJGU7v++dbpFAGaN4MHYwDAYDVR0TAQH/BAIw +ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDBDAOBgNVHQ8BAf8EBAMCBsAwHQYDVR0O +BBYEFPdYlqBP9YyJZuIQghW0gTb6TI9uMB8GA1UdIwQYMBaAFDcn53MUrYZBqYUx +oqeJvj0rFGIeMAoGCCqGSM49BAMCA4GMADCBiAJCAbEqb2ZnIcX1sFMwFoxxUwn3 +hRQlihDF/GSIwuTYVkJ4ycoaiirRAPgGJ7FMHq9Qtbcanxsgn3II7742VdupmxGQ +AkIBSmjs1lhY1PKb7kTY6eRByqaFcRPzm+irCTYcYJFtc5v2XyahDHmwqj4LIN0U +9Fu2Ln66BVmhey5Nr6LpKr711HI= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIC8TCCAlKgAwIBAgIUDdNECy3zl5g1DaFszb1PNbB2vIowCgYIKoZIzj0EAwIw +dzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTb21ld2hlcmUx +GjAYBgNVBAoMEUMyUEEgVGVzdCBSb290IENBMRkwFwYDVQQLDBBGT1IgVEVTVElO +R19PTkxZMRAwDgYDVQQDDAdSb290IENBMB4XDTI0MDgxMTIzMzQ1NloXDTM0MDgw +OTIzMzQ1NlowgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJ +U29tZXdoZXJlMScwJQYDVQQKDB5DMlBBIFRlc3QgSW50ZXJtZWRpYXRlIFJvb3Qg +Q0ExGTAXBgNVBAsMEEZPUiBURVNUSU5HX09OTFkxGDAWBgNVBAMMD0ludGVybWVk +aWF0ZSBDQTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEALHfLfnUzV9pGGbVakW4 +Ll1qEIEFGvit0Izg3TB7S3cZR3GclS8U8KnF2jpS+YayIKIm+v/FMttO4m3J2ImA +kQemAZXFP8U3Y6QsEWZXy9c9XO1KXDZ3HBckAEF+mooZOFnE+aAaUi/GCnryY5pT +Cm3jPT942Br6He4vmjipmAurJTGdo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHQ4EFgQUNyfncxSthkGphTGip4m+PSsUYh4wHwYDVR0j +BBgwFoAU2JkxamcaxDE0b1im5Se7NVvHW1IwCgYIKoZIzj0EAwIDgYwAMIGIAkIA +/C6yoJldk5drr/Kj4WAk6C2gRDweiYkJR6pXC7x/uBJVkQ/nQ/OvvEJqBxOtE47/ +n1NoKSWg/7XSSYTWLU+WjvYCQgHcS/7MrHaddhTKXPsbqvrduvoe5IkfQI/t1lv4 +i90/UXJqmSjryknReRCuC9A+wwWNx8tCnrM9wN4FRpPHgPfnPg== +-----END CERTIFICATE----- diff --git a/tests/fixtures/es512_private.key b/tests/fixtures/es512_private.key new file mode 100644 index 0000000..c780bdb --- /dev/null +++ b/tests/fixtures/es512_private.key @@ -0,0 +1,8 @@ +-----BEGIN PRIVATE KEY----- +MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIALdmsWsvFcOrDKvpB +Kn4f9e5uLcQhNBpG5huD0RNvMfWudt1kEe3+rgyAEL4oUNOhvbEfrd5+njpeGvBu +BTWDA5ahgYkDgYYABAAptbysTzJHzOX9BAYOjkTXS5vVJA3ruVv1JkDot6Z3GMST +wdXVulcyNGlW3qjPTMASyTewGB2rek0ARv6nnshx2wGnLu8YAWL3JRmMFoOoR7e7 +FOEz6OOOCeMNR9yyUCYbMMxTLQo8CN/aOr+FPlldN+ol6J83eL5LJGU7v++dbpFA +GQ== +-----END PRIVATE KEY----- diff --git a/tests/fixtures/ps256_certs.pem b/tests/fixtures/ps256_certs.pem new file mode 100644 index 0000000..8e8302e --- /dev/null +++ b/tests/fixtures/ps256_certs.pem @@ -0,0 +1,76 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +MFDu+94= +-----END CERTIFICATE----- diff --git a/tests/fixtures/ps256_private.key b/tests/fixtures/ps256_private.key new file mode 100644 index 0000000..716f141 --- /dev/null +++ b/tests/fixtures/ps256_private.key @@ -0,0 +1,53 @@ +-----BEGIN PRIVATE KEY----- +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 +zWHbAWccItOAAhBSePcOd1UYP77J1QRYnBs= +-----END PRIVATE KEY----- diff --git a/tests/fixtures/ps384_certs.pem b/tests/fixtures/ps384_certs.pem new file mode 100644 index 0000000..86cc8ab --- /dev/null +++ b/tests/fixtures/ps384_certs.pem @@ -0,0 +1,76 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +Ar6Kg4A= +-----END CERTIFICATE----- diff --git a/tests/fixtures/ps384_private.key b/tests/fixtures/ps384_private.key new file mode 100644 index 0000000..4835355 --- /dev/null +++ b/tests/fixtures/ps384_private.key @@ -0,0 +1,53 @@ +-----BEGIN PRIVATE KEY----- +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 +J6S/r1T+mXY4UXqQZpqORjvP7S2jVGZXEME= +-----END PRIVATE KEY----- diff --git a/tests/fixtures/ps512_certs.pem b/tests/fixtures/ps512_certs.pem new file mode 100644 index 0000000..7dec526 --- /dev/null +++ b/tests/fixtures/ps512_certs.pem @@ -0,0 +1,76 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +y7J3R/w= +-----END CERTIFICATE----- diff --git a/tests/fixtures/ps512_private.key b/tests/fixtures/ps512_private.key new file mode 100644 index 0000000..6761a24 --- /dev/null +++ b/tests/fixtures/ps512_private.key @@ -0,0 +1,53 @@ +-----BEGIN PRIVATE KEY----- +MIIJdQIBADBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAwUAoRwwGgYJKoZI +hvcNAQEIMA0GCWCGSAFlAwQCAwUAogMCASAEggkrMIIJJwIBAAKCAgEA3G9Et5gI +btpO24/7xjk25+Y917B8fJZApFc6kSUhDYvdTHecJcfLXoF3uMr1UyzxwTiD2pkj +GyoT3UZPeojGBvxYWi9lGFkADNYwWKAziF3FwvC1Al2ZeRxar7D6G3sJCf+sJJSf +SIV7AmdzQ5hemB95/jrihc1VJqKYxgDSspqiUuze0f87wL8goCjZykkof4t8ppDH +v8S4D0pLAkkbuwbFrmNdgQCRmEKCYZbTC/DTmoNvQxfDOnoyZ8WZMYuIi0yHAHWV +eboCIGATEEUAdf7RLQQds5hH7AHDkaz2h0QTgGMl/yc/9t3m1SieEDfGGHEZCMkK +Bf1/peIcaUKOaIfgcb/tbFHsNGI8vFORTEVJikmaP2e8DAtqu4T6jGoHTg/R4VQ4 +BMlT9n4FQbYcCEV4sUVAm1Gtj7Fx3uHGDySo7gcQlIXs1zafd8tZNk5l7ZC7dPn+ +1T1cUb5pYR11e76kM7cFwi8P1VGrXfxfwQxfX91n2uWMfSIb9syy8ULMY1rjN1Pe +559nS/ZoHhfzoveH5kCvx6EJ/X1NEWMkPYiB1Vwri69PT1LesQUxvQv9LPoEv2Cw +lEsuAGieHl6rDZC3vwf1cwSV+TF73TWVCJhXIRwVTNvxwChgkUMJtFun/tJyD4Ui +TD+XvK9ryK4ZVmSSBPu8fNypnphyv93vnqsCAwEAAQKCAgAAyft2jsPOcZ7QrKFZ +3joAlXz/qOqXq346vRj3n4xhPPopOdBMnfLhrVzX8z4Fg1v8A32azyktgc5Ap8fA +fagOwodRK3fZ2dkIn8QCM2v/8igzVKC/wBEW6crTv5qoPLVoezd6UKI9GJoaBQI3 +hAkoH3TeDG/L+p8c3ZqHrgMvfs23FTIS5tQ18QPbMdybem7tZctleqLGw/9b/Z02 +yJo+Mi+VW5kisOc4D8KotWRc6r68L3UZxTXMu/MyytBvUSrQ2zHglQGt3bFe4qDa +GVVsjTbYkNQTf5SAGZ/QAKb7+8/4DpSSLj7p8GvMQjlinC3UPoevIZtN2wle8kbj +bE14XYmJmCuktUFrvQLCbmBtgwI36hfPWo/G8n+hkAECdaXF4McRLEr2CZSUSGh3 +FEEZTrjB2+k9hzdovT/nYd9t7FTJDq3mLRCz7HmkuKBCx1oSwZWactefDQzC5wLd +ZW2NU2IDTG3xRsYrJ79ZzEKCYg+QrZPWYYaUHjth9pGRe0jU0IL2bt5IjbC40IjZ +fxoYtYTOJX97gu6cQfWFQ6EqVzzyj2Fuk8ZzjJdg32ftlF9yFlEDXjEGOQ+SaN4i +rgoN8LjHMPy8nJca9Z4gXgAfKTrHs+uiA14fWdGc6zGAy0eplkrCFbHesAJu6lCV +CvX6ntKZUmz8CcqNKiZSJ18XAQKCAQEA7hIvqzHrqGs740LzraYWMlE1eI74Uagl +dx87rVUf55SRM3GLq2/Cj0G9QAUs12lWV/cdTORWZ8X31I8LqIvI1Staf5s94mM5 +dCKSTR5DKY4XkTg50ifjPmAdL8Lw6m3e/LvNT2vdZyxE+AMgHQs2w+bwDVoiXlun +blkRZ5AiKm0k28cRSpKMLW6K5KMnPx+UMYqZSCUiXVkXnTmOF8f/a1/xuTtBWn0S +J4B3Y0N0JSuqyPRNMPyGojv/C+qfLujSM9gG3X5rG4RJ2atZB9Sj7xwoYapF3sem +xJoR0isFPiqqXd2JPgxIrk6+8ZRjVOUUApW1xi17jXEb3Dl7G1PbAQKCAQEA7QkR +Jd+ral5bjyVmUcGzTdAYp7ZVIX64lWbbkdKiDmQjbphKEfhsKGGi7yIMXVJOh1rU +A4d2rWyFs+KuXJ5lNcxgHPBFpANp6KG09S+It2K47P3uLKykGhG9m1+u39dbY3X0 +dok3z0PSAMmJ5yjpnjJ4yr3gRNMqjGQzj1IBUObJG0DKAKzDbx/qcTlR2Eg/xy+5 +5Skz8G8wt8wW8kp4Uqg+Q4k0a4GM2euDFrMGTgDX9bL0mbN616wmwX/zKbPnsfEi +V7FKFbf6qjKh1GpN4oysazE9Ef6SJd8JSEW/nvtnTOUdcGyueT6vcKmDUDUE+p1Y +s4QxEWGgJMP7bjVVqwKCAQAOnh8/K7mEhdEPizTbHK+QN1xJIr4shV6HietoISns +Vjr0Jaylwh74HNk57RdrwpQCorGx3vNPO+CTtJTKrIqQTDEqVKXgGKeWcej5wzns +e0UHLaSfOsPBOLWSES4ptsVST1Wz4rOdeiJh+A8ouV+Ld8qVh0tE7+u/4sgmy7rN +UBZLSFXPdmAVCgyNLVdjkNTQleoDfAQ4JxhEFxyaaTVNqn51GSZSIAvVURYDiki/ +X4cE/7sdnuPFYlmuqaFMiA8gD8ucHf8FmYHPU64LxQqry9bO6F2R6EBdIfimJu4T +exsq2Eds/+QWj885lhrjQ42O+xJGMNS9QoyrYPwmGWgBAoIBAGWJ3sikmd+E8B/P +25bmLRMGM6mUV79zc0q8tw4udrFHgE4d+ozcKCjcOlnJ8hX/7hfZdrzUSKhdtqCq +WBrg12ZGWF3NJ2fs9m9q5gOqCxzTs3gCBwcoJVvtgZWPVXAZ5tIic0hb24Zz/P/N +Vb+qLPlRkaD32ZxiAPYG7ndUn6+yTS/Zfy7u0wFFYL6D2WWW/YssDKv4DIHQf8Qc +LzA3aneuDo4LtmLfKiEn3A3bPrYncluT/2fgFJ1S5384ekfBA12ceCOslJndKAnE +isJdj2Oe8zUv8ING1ABZHLbS+hT2iPfNpeDFDbBug+T1GIVBURKyWI79768MbPeS +5J//ROUCggEAEZnGMK1vAxw42oY9qfJyf4kPIlE7MyBtcpzeslrpLepjB3ov2Lk2 +cSfNaED8DVd8MYn7J0ZnH0+ZgXp2Da5bQ9R8GfTkeUAiPk0DJJTBbNtF4kAUVt15 +2bXiCp9fDv7OUrIBmTc1e6Io3VgNlBxPoXW6xNOz/6QR8k/C34tQ06OsW8QEVMGt +Tqf38d2Ln1pMb1II5JXPfpr444I+Y19oOqZPxia6jpg9D+3BcPjE1vJ4bttJJtRf +MM00EvB4ourFj3xN93/ZYsd9X+CpPLXDMQ+OyJW15YZ70T1EsohTwLbHuVE3T64x +rjlndR12IH3mG/TIuDC9gTNXfl9q4TGxSg== +-----END PRIVATE KEY----- diff --git a/tests/fixtures/screenshot-signed-badsig.jpg b/tests/fixtures/screenshot-signed-badsig.jpg new file mode 100644 index 0000000..ba2aeef Binary files /dev/null and b/tests/fixtures/screenshot-signed-badsig.jpg differ diff --git a/tests/fixtures/video.mp4 b/tests/fixtures/video.mp4 new file mode 100644 index 0000000..62b4c60 Binary files /dev/null and b/tests/fixtures/video.mp4 differ