c2pa-python


Namec2pa-python JSON
Version 0.6.1 PyPI version JSON
download
home_pageNone
SummaryPython bindings for the C2PA Content Authenticity Initiative (CAI) library
upload_time2024-11-06 19:51:06
maintainerNone
docs_urlNone
authorGavin Peacock <gpeacock@adobe.com
requires_python>=3.7
licenseNone
keywords c2pa cai content credentials metadata provenance
VCS
bugtrack_url
requirements maturin uniffi-bindgen cryptography
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # C2PA Python

This repository implements Python bindings for the Content Authenticity Initiative (CAI) library.

This library enables you to read and validate C2PA data in supported media files and add signed manifests to supported media files.

**NOTE**: Starting with version 0.5.0, this package has a completely different API from version 0.4.0. See [Release notes](#release-notes) for more information.

**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:

```bash
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.

### Updating

Determine what version you've got by entering this command:

```bash
pip list | grep c2pa-python
```

If the version shown is lower than the most recent version, then update by [reinstalling](#installation).

### 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:

```bash
pip install --upgrade --force-reinstall c2pa-python
```

## Usage

### Import

Import the API as follows:

```py
from c2pa import *
```

### Define manifest JSON

The Python library works with both file-based and stream-based operations.
In both cases, the manifest JSON string defines the C2PA manifest to add to an asset; for example:

```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" }
        }
      }
    }
  ]
 })
```

### Signing function

The `sign_ps256` function is [defined in the library](https://github.com/contentauth/c2pa-python/blob/main/c2pa/c2pa_api/c2pa_api.py#L209) and is reproduced here to show how signing is performed.

```py
# Example of using Python crypto to sign data using openssl with Ps256
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding

def sign_ps256(data: bytes, key_path: str) -> bytes:
    with open(key_path, "rb") as key_file:
        private_key = serialization.load_pem_private_key(
            key_file.read(),
            password=None,
        )
    signature = private_key.sign(
        data,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    return signature
```

### File-based operation

**Read and validate C2PA data from an asset file**

Use the `Reader` to read C2PA data from the specified asset file (see [supported file formats](#supported-file-formats)).

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.

An asset 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")

  # 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)
```

**Add a signed manifest to an asset file**

**WARNING**: This example accesses the private key and security certificate directly from the local file system.  This is fine during development, but doing so in production may be insecure. Instead use a Key Management Service (KMS) or a hardware security module (HSM) to access the certificate and key; for example as show in the [C2PA Python Example](https://github.com/contentauth/c2pa-python-example).

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")

  # 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")

  # 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"
    }
  }

  # Add the ingredient to the builder loading information  from a source file.
  builder.add_ingredient_file(ingredient_json, "tests/fixtures/A.jpg")

  # 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")

except Exception as err:
    print(err)
```

### Stream-based operation

Instead of working with files, you can read, validate, and add a signed manifest to streamed data.  This example code does the same thing as the file-based example.

**Read and validate C2PA data from a stream**

```py
try:
  # 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)
```

**Add a signed manifest to a stream**

**WARNING**: This example accesses the private key and security certificate directly from the local file system.  This is fine during development, but doing so in production may be insecure. Instead use a Key Management Service (KMS) or a hardware security module (HSM) to access the certificate and key; for example as show in the [C2PA Python Example](https://github.com/contentauth/c2pa-python-example).

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")

  # Create a builder add a thumbnail resource and an ingredient file.
  builder = Builder(manifest_json)

  # 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"
    }
  }

  # 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 the builder with a stream and output it to a stream
  # This returns the binary manifest data that could be uploaded to cloud storage.
  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)
 ```

## 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`                                 |
 | `gif`         | `image/gif`                                         |
 | `heic`        | `image/heic`                                        |
 | `heif`        | `image/heif`                                        |
 | `jpg`, `jpeg` | `image/jpeg`                                        |
 | `m4a`         | `audio/mp4`                                         |
 | `mp3`         | `audio/mpeg`                                        |
 | `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`:

```bash
apt update
apt install curl
```

Install Rust:

```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
```

Install Python, `pip`, and `venv`:

```bash
apt install python3
apt install pip
apt install python3.11-venv
python3 -m venv .venv
```

Build the wheel for your platform (from the root of the repository):

```bash
source .venv/bin/activate
pip install -r requirements.txt
python3 -m pip install build
pip install -U pytest

python3 -m build --wheel
```

Note: To peek at the Python code (uniffi generated and non-generated), run `maturin develop` and look in the c2pa folder.

### ManyLinux build

Build using [manylinux](https://github.com/pypa/manylinux) by using a Docker image as follows:

```bash
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 following these steps:

1. Activate the virtual environment: `source .venv/bin/activate`
2. (optional) Install dependencies: `pip install -r requirements.txt`
3. Setup the virtual environment with local changes: `maturin develop`
4. Run the tests: `pytest`
5. Deactivate the virtual environment: `deactivate`

For example:

```bash
source .venv/bin/activate
maturin develop
python3 tests/training.py
deactivate
```

## Release notes

### Version 0.5.2

New features:

- Allow EC signatures in DER format from signers and verify signature format during validation.
- Fix bug in signing audio and video files in ISO Base Media File Format (BMFF).
- Add the ability to verify PDF files (but not to sign them).
- Increase speed of `sign_file` by 2x or more, when using file paths (uses native Rust file I/O).
- Fixes for RIFF and GIF formats.

### Version 0.5.0

New features in this release:

- Rewrites the API to be stream-based using a Builder / 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).


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "c2pa-python",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "Gavin Peacock <gpeacock@adobe.com>",
    "keywords": "C2PA, CAI, content credentials, metadata, provenance",
    "author": "Gavin Peacock <gpeacock@adobe.com",
    "author_email": "Gavin Peacock <gpeacock@adobe.com>",
    "download_url": "https://files.pythonhosted.org/packages/24/24/e0f8e249e004e97ea69f77e5a70448500f8ff24882432647864bab946b8a/c2pa_python-0.6.1.tar.gz",
    "platform": null,
    "description": "# C2PA Python\n\nThis repository implements Python bindings for the Content Authenticity Initiative (CAI) library.\n\nThis library enables you to read and validate C2PA data in supported media files and add signed manifests to supported media files.\n\n**NOTE**: Starting with version 0.5.0, this package has a completely different API from version 0.4.0. See [Release notes](#release-notes) for more information.\n\n**WARNING**: This is an prerelease version of this library.  There may be bugs and unimplemented features, and the API is subject to change.\n\n## Installation\n\nInstall from PyPI by entering this command:\n\n```bash\npip install -U c2pa-python\n```\n\nThis 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.\n\n### Updating\n\nDetermine what version you've got by entering this command:\n\n```bash\npip list | grep c2pa-python\n```\n\nIf the version shown is lower than the most recent version, then update by [reinstalling](#installation).\n\n### Reinstalling\n\nIf 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:\n\n```bash\npip install --upgrade --force-reinstall c2pa-python\n```\n\n## Usage\n\n### Import\n\nImport the API as follows:\n\n```py\nfrom c2pa import *\n```\n\n### Define manifest JSON\n\nThe Python library works with both file-based and stream-based operations.\nIn both cases, the manifest JSON string defines the C2PA manifest to add to an asset; for example:\n\n```py\nmanifest_json = json.dumps({\n    \"claim_generator\": \"python_test/0.1\",\n    \"assertions\": [\n    {\n      \"label\": \"c2pa.training-mining\",\n      \"data\": {\n        \"entries\": {\n          \"c2pa.ai_generative_training\": { \"use\": \"notAllowed\" },\n          \"c2pa.ai_inference\": { \"use\": \"notAllowed\" },\n          \"c2pa.ai_training\": { \"use\": \"notAllowed\" },\n          \"c2pa.data_mining\": { \"use\": \"notAllowed\" }\n        }\n      }\n    }\n  ]\n })\n```\n\n### Signing function\n\nThe `sign_ps256` function is [defined in the library](https://github.com/contentauth/c2pa-python/blob/main/c2pa/c2pa_api/c2pa_api.py#L209) and is reproduced here to show how signing is performed.\n\n```py\n# Example of using Python crypto to sign data using openssl with Ps256\nfrom cryptography.hazmat.primitives import hashes, serialization\nfrom cryptography.hazmat.primitives.asymmetric import padding\n\ndef sign_ps256(data: bytes, key_path: str) -> bytes:\n    with open(key_path, \"rb\") as key_file:\n        private_key = serialization.load_pem_private_key(\n            key_file.read(),\n            password=None,\n        )\n    signature = private_key.sign(\n        data,\n        padding.PSS(\n            mgf=padding.MGF1(hashes.SHA256()),\n            salt_length=padding.PSS.MAX_LENGTH\n        ),\n        hashes.SHA256()\n    )\n    return signature\n```\n\n### File-based operation\n\n**Read and validate C2PA data from an asset file**\n\nUse the `Reader` to read C2PA data from the specified asset file (see [supported file formats](#supported-file-formats)).\n\nThis 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.\n\nAn asset 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`.\n\nNOTE: For a comprehensive reference to the JSON manifest structure, see the [Manifest store reference](https://opensource.contentauthenticity.org/docs/manifest/manifest-ref).\n\n```py\ntry:\n  # Create a reader from a file path\n  reader = c2pa.Reader.from_file(\"path/to/media_file.jpg\")\n\n  # Print the JSON for a manifest. \n  print(\"manifest store:\", reader.json())\n\n  # Get the active manifest.\n  manifest = reader.get_active_manifest()\n  if manifest != None:\n\n    # get the uri to the manifest's thumbnail and write it to a file\n    uri = manifest[\"thumbnail\"][\"identifier\"]\n    reader.resource_to_file(uri, \"thumbnail_v2.jpg\") \n\nexcept Exception as err:\n    print(err)\n```\n\n**Add a signed manifest to an asset file**\n\n**WARNING**: This example accesses the private key and security certificate directly from the local file system.  This is fine during development, but doing so in production may be insecure. Instead use a Key Management Service (KMS) or a hardware security module (HSM) to access the certificate and key; for example as show in the [C2PA Python Example](https://github.com/contentauth/c2pa-python-example).\n\nUse a `Builder` to add a manifest to an asset:\n\n```py\ntry:\n  # Define a function to sign the claim bytes\n  # In this case we are using a pre-defined sign_ps256 method, passing in our private cert\n  # Normally this cert would be kept safe in some other location\n  def private_sign(data: bytes) -> bytes:\n    return sign_ps256(data, \"tests/fixtures/ps256.pem\")\n\n  # read our public certs into memory\n  certs = open(data_dir + \"ps256.pub\", \"rb\").read()\n\n  # Create a signer from the private signer, certs and a time stamp service url\n  signer = create_signer(private_sign, SigningAlg.PS256, certs, \"http://timestamp.digicert.com\")\n\n  # Create a builder add a thumbnail resource and an ingredient file.\n  builder = Builder(manifest_json)\n\n  # The uri provided here \"thumbnail\" must match an identifier in the manifest definition.\n  builder.add_resource_file(\"thumbnail\", \"tests/fixtures/A_thumbnail.jpg\")\n\n  # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail\n  ingredient_json = {\n    \"title\": \"A.jpg\",\n    \"relationship\": \"parentOf\", # \"parentOf\", \"componentOf\" or \"inputTo\"\n    \"thumbnail\": {\n        \"identifier\": \"thumbnail\",\n        \"format\": \"image/jpeg\"\n    }\n  }\n\n  # Add the ingredient to the builder loading information  from a source file.\n  builder.add_ingredient_file(ingredient_json, \"tests/fixtures/A.jpg\")\n\n  # At this point we could archive or unarchive our Builder to continue later.\n  # In this example we use a bytearray for the archive stream.\n  # all ingredients and resources will be saved in the archive\n  archive = io.BytesIO(bytearray())\n  builder.to_archive(archive)\n  archive.seek()\n  builder = builder.from_archive(archive)\n\n  # Sign and add our manifest to a source file, writing it to an output file.\n  # This returns the binary manifest data that could be uploaded to cloud storage.\n  c2pa_data = builder.sign_file(signer, \"tests/fixtures/A.jpg\", \"target/out.jpg\")\n\nexcept Exception as err:\n    print(err)\n```\n\n### Stream-based operation\n\nInstead of working with files, you can read, validate, and add a signed manifest to streamed data.  This example code does the same thing as the file-based example.\n\n**Read and validate C2PA data from a stream**\n\n```py\ntry:\n  # It's also possible to create a reader from a format and stream\n  # Note that these two readers are functionally equivalent\n  stream = open(\"path/to/media_file.jpg\", \"rb\")\n  reader = c2pa.Reader(\"image/jpeg\", stream)\n\n  # Print the JSON for a manifest. \n  print(\"manifest store:\", reader.json())\n\n  # Get the active manifest.\n  manifest = reader.get_active_manifest()\n  if manifest != None:\n\n    # get the uri to the manifest's thumbnail and write it to a file\n    uri = manifest[\"thumbnail\"][\"identifier\"]\n    reader.resource_to_file(uri, \"thumbnail_v2.jpg\") \n\nexcept Exception as err:\n    print(err)\n```\n\n**Add a signed manifest to a stream**\n\n**WARNING**: This example accesses the private key and security certificate directly from the local file system.  This is fine during development, but doing so in production may be insecure. Instead use a Key Management Service (KMS) or a hardware security module (HSM) to access the certificate and key; for example as show in the [C2PA Python Example](https://github.com/contentauth/c2pa-python-example).\n\nUse a `Builder` to add a manifest to an asset:\n\n```py\ntry:\n  # Define a function to sign the claim bytes\n  # In this case we are using a pre-defined sign_ps256 method, passing in our private cert\n  # Normally this cert would be kept safe in some other location\n  def private_sign(data: bytes) -> bytes:\n    return sign_ps256(data, \"tests/fixtures/ps256.pem\")\n\n  # read our public certs into memory\n  certs = open(data_dir + \"ps256.pub\", \"rb\").read()\n\n  # Create a signer from the private signer, certs and a time stamp service url\n  signer = create_signer(private_sign, SigningAlg.PS256, certs, \"http://timestamp.digicert.com\")\n\n  # Create a builder add a thumbnail resource and an ingredient file.\n  builder = Builder(manifest_json)\n\n  # Add the resource from a stream\n  a_thumbnail_jpg_stream = open(\"tests/fixtures/A_thumbnail.jpg\", \"rb\")\n  builder.add_resource(\"image/jpeg\", a_thumbnail_jpg_stream)\n\n  # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail\n  ingredient_json = {\n    \"title\": \"A.jpg\",\n    \"relationship\": \"parentOf\", # \"parentOf\", \"componentOf\" or \"inputTo\"\n    \"thumbnail\": {\n        \"identifier\": \"thumbnail\",\n        \"format\": \"image/jpeg\"\n    }\n  }\n\n  # Add the ingredient from a stream\n  a_jpg_stream = open(\"tests/fixtures/A.jpg\", \"rb\")\n  builder.add_ingredient(\"image/jpeg\", a_jpg_stream)\n\n  # At this point we could archive or unarchive our Builder to continue later.\n  # In this example we use a bytearray for the archive stream.\n  # all ingredients and resources will be saved in the archive\n  archive = io.BytesIO(bytearray())\n  builder.to_archive(archive)\n  archive.seek()\n  builder = builder.from_archive(archive)\n\n  # Sign the builder with a stream and output it to a stream\n  # This returns the binary manifest data that could be uploaded to cloud storage.\n  input_stream = open(\"tests/fixtures/A.jpg\", \"rb\")\n  output_stream = open(\"target/out.jpg\", \"wb\")\n  c2pa_data = builder.sign(signer, \"image/jpeg\", input_stream, output_stream)\n\nexcept Exception as err:\n    print(err)\n ```\n\n## Supported file formats\n\n | Extensions    | MIME type                                           |\n | ------------- | --------------------------------------------------- |\n | `avi`         | `video/msvideo`, `video/avi`, `application-msvideo` |\n | `avif`        | `image/avif`                                        |\n | `c2pa`        | `application/x-c2pa-manifest-store`                 |\n | `dng`         | `image/x-adobe-dng`                                 |\n | `gif`         | `image/gif`                                         |\n | `heic`        | `image/heic`                                        |\n | `heif`        | `image/heif`                                        |\n | `jpg`, `jpeg` | `image/jpeg`                                        |\n | `m4a`         | `audio/mp4`                                         |\n | `mp3`         | `audio/mpeg`                                        |\n | `mp4`         | `video/mp4`, `application/mp4`                      |\n | `mov`         | `video/quicktime`                                   |\n | `png`         | `image/png`                                         |\n | `svg`         | `image/svg+xml`                                     |\n | `tif`,`tiff`  | `image/tiff`                                        |\n | `wav`         | `audio/x-wav`                                       |\n | `webp`        | `image/webp`                                        |\n\n\n## Development\n\nIt is best to [set up a virtual environment](https://virtualenv.pypa.io/en/latest/installation.html) for development and testing.\n\nTo build from source on Linux, install `curl` and `rustup` then set up Python.\n\nFirst update `apt` then (if needed) install `curl`:\n\n```bash\napt update\napt install curl\n```\n\nInstall Rust:\n\n```bash\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\nsource \"$HOME/.cargo/env\"\n```\n\nInstall Python, `pip`, and `venv`:\n\n```bash\napt install python3\napt install pip\napt install python3.11-venv\npython3 -m venv .venv\n```\n\nBuild the wheel for your platform (from the root of the repository):\n\n```bash\nsource .venv/bin/activate\npip install -r requirements.txt\npython3 -m pip install build\npip install -U pytest\n\npython3 -m build --wheel\n```\n\nNote: To peek at the Python code (uniffi generated and non-generated), run `maturin develop` and look in the c2pa folder.\n\n### ManyLinux build\n\nBuild using [manylinux](https://github.com/pypa/manylinux) by using a Docker image as follows:\n\n```bash\ndocker run -it quay.io/pypa/manylinux_2_28_aarch64 bash\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\nsource \"$HOME/.cargo/env\"\nexport PATH=/opt/python/cp312-cp312/bin:$PATH\npip install maturin\npip install venv\npip install build\npip install -U pytest\n\ncd home\ngit clone https://github.com/contentauth/c2pa-python.git\ncd c2pa-python\npython3 -m build --wheel\nauditwheel repair target/wheels/c2pa_python-0.4.0-py3-none-linux_aarch64.whl\n```\n\n### Testing\n\nWe use [PyTest](https://docs.pytest.org/) for testing.\n\nRun tests by following these steps:\n\n1. Activate the virtual environment: `source .venv/bin/activate`\n2. (optional) Install dependencies: `pip install -r requirements.txt`\n3. Setup the virtual environment with local changes: `maturin develop`\n4. Run the tests: `pytest`\n5. Deactivate the virtual environment: `deactivate`\n\nFor example:\n\n```bash\nsource .venv/bin/activate\nmaturin develop\npython3 tests/training.py\ndeactivate\n```\n\n## Release notes\n\n### Version 0.5.2\n\nNew features:\n\n- Allow EC signatures in DER format from signers and verify signature format during validation.\n- Fix bug in signing audio and video files in ISO Base Media File Format (BMFF).\n- Add the ability to verify PDF files (but not to sign them).\n- Increase speed of `sign_file` by 2x or more, when using file paths (uses native Rust file I/O).\n- Fixes for RIFF and GIF formats.\n\n### Version 0.5.0\n\nNew features in this release:\n\n- Rewrites the API to be stream-based using a Builder / Reader model.\n- The functions now support throwing `c2pa.Error` values, caught with `try`/`except`.\n- Instead of `c2pa.read_file` you now call `c2pa_api.Reader.from_file` and `reader.json`.\n- Read thumbnails and other resources use `reader.resource_to_stream` or `reader.resource.to_file`.\n- Instead of `c2pa.sign_file` use `c2pa_api.Builder.from_json` and `builder.sign` or `builder.sign_file`.\n- Add thumbnails or other resources with `builder.add_resource` or `builder.add_resource_file`.\n- Add Ingredients with `builder.add_ingredient` or `builder.add_ingredient_file`.\n- You can archive a `Builder` using `builder.to_archive` and reconstruct it with `builder.from_archive`.\n- Signers can be constructed with `c2pa_api.create_signer`.\n- The signer now requires a signing function to keep private keys private.\n- Example signing functions are provided in c2pa_api.py\n\n### Version 0.4.0\n\nThis release:\n\n- Changes the name of the import from `c2pa-python` to `c2pa`.\n- Supports pre-built platform wheels for macOS, Windows and [manylinux](https://github.com/pypa/manylinux).\n\n### Version 0.3.0\n\nThis release includes some breaking changes to align with future APIs:\n\n- `C2paSignerInfo` moves the `alg` to the first parameter from the 3rd.\n- `c2pa.verify_from_file_json` is now `c2pa.read_file`.\n- `c2pa.ingredient_from_file_json` is now `c2pa.read_ingredient_file`.\n- `c2pa.add_manifest_to_file_json` is now `c2pa.sign_file`.\n- 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\")).\n- The ingredient thumbnail identifier may be jumbf uri reference if a valid thumb already exists in the active manifest.\n- Extracted file paths for read_file now use a folder structure and different naming conventions.\n\n## License\n\nThis 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).\n\nNote that some components and dependent crates are licensed under different terms; please check the license terms for each crate and component for details.\n\n### Contributions and feedback\n\nWe 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).\n\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Python bindings for the C2PA Content Authenticity Initiative (CAI) library",
    "version": "0.6.1",
    "project_urls": {
        "homepage": "https://contentauthenticity.org",
        "repository": "https://github.com/contentauth/c2pa-python"
    },
    "split_keywords": [
        "c2pa",
        " cai",
        " content credentials",
        " metadata",
        " provenance"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "4948b1322835538dd8756951384af56741343a205635d1ed453d50ba7ff3b751",
                "md5": "b0f0773658e122e97a87e753fa4def7b",
                "sha256": "9900b42d24eef8c35c47d3204913aeddc7b1441a6a700487e453100b64bad6da"
            },
            "downloads": -1,
            "filename": "c2pa_python-0.6.1-py3-none-macosx_10_12_x86_64.whl",
            "has_sig": false,
            "md5_digest": "b0f0773658e122e97a87e753fa4def7b",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 5459785,
            "upload_time": "2024-11-06T19:50:55",
            "upload_time_iso_8601": "2024-11-06T19:50:55.178516Z",
            "url": "https://files.pythonhosted.org/packages/49/48/b1322835538dd8756951384af56741343a205635d1ed453d50ba7ff3b751/c2pa_python-0.6.1-py3-none-macosx_10_12_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f612ff0b79eab440adc2aa9f3d4bf77cb80ae0daf558dc1b6f8894d874cf5a5c",
                "md5": "001fd1982f5d785059e3af96e97ce7b2",
                "sha256": "0628c52703ec6d3d67623e27e1b6ed59a9154d8ac10107ad3a809a360f2a55db"
            },
            "downloads": -1,
            "filename": "c2pa_python-0.6.1-py3-none-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "001fd1982f5d785059e3af96e97ce7b2",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 5579971,
            "upload_time": "2024-11-06T19:50:57",
            "upload_time_iso_8601": "2024-11-06T19:50:57.002766Z",
            "url": "https://files.pythonhosted.org/packages/f6/12/ff0b79eab440adc2aa9f3d4bf77cb80ae0daf558dc1b6f8894d874cf5a5c/c2pa_python-0.6.1-py3-none-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "932cfb2e3ce7f6c8a6d14c3e2e692e478251f0f4f1f928c578f61e1e83aac16b",
                "md5": "763aa4a7cc5ab8efa5f8f909a7d5e895",
                "sha256": "c059cab7418b98525d215f458a75bc06888ed1e489aad1682907524e654ebde6"
            },
            "downloads": -1,
            "filename": "c2pa_python-0.6.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "has_sig": false,
            "md5_digest": "763aa4a7cc5ab8efa5f8f909a7d5e895",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 6058800,
            "upload_time": "2024-11-06T19:50:58",
            "upload_time_iso_8601": "2024-11-06T19:50:58.692846Z",
            "url": "https://files.pythonhosted.org/packages/93/2c/fb2e3ce7f6c8a6d14c3e2e692e478251f0f4f1f928c578f61e1e83aac16b/c2pa_python-0.6.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "07285ab43da18a294eeca6bdab6a8d361dcd5781fa4ac97ee850ad9416680e43",
                "md5": "a4458dad01807518689822da803e3dae",
                "sha256": "fd2fc5c78c381bd7acb4a87fff679ee8c039949a67647625f0baefda70d8b2a9"
            },
            "downloads": -1,
            "filename": "c2pa_python-0.6.1-py3-none-manylinux_2_28_aarch64.whl",
            "has_sig": false,
            "md5_digest": "a4458dad01807518689822da803e3dae",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 6099217,
            "upload_time": "2024-11-06T19:51:00",
            "upload_time_iso_8601": "2024-11-06T19:51:00.833604Z",
            "url": "https://files.pythonhosted.org/packages/07/28/5ab43da18a294eeca6bdab6a8d361dcd5781fa4ac97ee850ad9416680e43/c2pa_python-0.6.1-py3-none-manylinux_2_28_aarch64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "5d2f7e361161980464e3573d21b6e6b4fe3fa1a507a0368ed1f48b6b960ca487",
                "md5": "2d094ed568c91d74d32a989151a3bcd8",
                "sha256": "cbc894f0f4435f4ef3915ac8273df295d774ebe992b7d2feeff56256752546f5"
            },
            "downloads": -1,
            "filename": "c2pa_python-0.6.1-py3-none-win32.whl",
            "has_sig": false,
            "md5_digest": "2d094ed568c91d74d32a989151a3bcd8",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 4803348,
            "upload_time": "2024-11-06T19:51:02",
            "upload_time_iso_8601": "2024-11-06T19:51:02.402103Z",
            "url": "https://files.pythonhosted.org/packages/5d/2f/7e361161980464e3573d21b6e6b4fe3fa1a507a0368ed1f48b6b960ca487/c2pa_python-0.6.1-py3-none-win32.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "103933c97009fc3728592f8b6b33b2d87d12ec2649e54466a84da43fec9a9826",
                "md5": "579b002bbf7c9df790ed0c5167c92855",
                "sha256": "ba159f6458dc5da92a20d84f7c8851e29c68b402e693a29c8272546e21c1421b"
            },
            "downloads": -1,
            "filename": "c2pa_python-0.6.1-py3-none-win_amd64.whl",
            "has_sig": false,
            "md5_digest": "579b002bbf7c9df790ed0c5167c92855",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 5378216,
            "upload_time": "2024-11-06T19:51:04",
            "upload_time_iso_8601": "2024-11-06T19:51:04.077219Z",
            "url": "https://files.pythonhosted.org/packages/10/39/33c97009fc3728592f8b6b33b2d87d12ec2649e54466a84da43fec9a9826/c2pa_python-0.6.1-py3-none-win_amd64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "2424e0f8e249e004e97ea69f77e5a70448500f8ff24882432647864bab946b8a",
                "md5": "87f084f8c140c0874a4a035cf79b6bf0",
                "sha256": "2f6d4cd9dfdbcb0920e4e909bf81ba281963cf54d85a95154ef4dabe40ef5d81"
            },
            "downloads": -1,
            "filename": "c2pa_python-0.6.1.tar.gz",
            "has_sig": false,
            "md5_digest": "87f084f8c140c0874a4a035cf79b6bf0",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 345265,
            "upload_time": "2024-11-06T19:51:06",
            "upload_time_iso_8601": "2024-11-06T19:51:06.137618Z",
            "url": "https://files.pythonhosted.org/packages/24/24/e0f8e249e004e97ea69f77e5a70448500f8ff24882432647864bab946b8a/c2pa_python-0.6.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-06 19:51:06",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "contentauth",
    "github_project": "c2pa-python",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "maturin",
            "specs": [
                [
                    "==",
                    "1.2.0"
                ]
            ]
        },
        {
            "name": "uniffi-bindgen",
            "specs": [
                [
                    "==",
                    "0.24.1"
                ]
            ]
        },
        {
            "name": "cryptography",
            "specs": [
                [
                    "==",
                    "43.0.1"
                ]
            ]
        }
    ],
    "lcname": "c2pa-python"
}
        
Elapsed time: 3.06962s