Skip to content

Commit 09030d4

Browse files
committed
fix: More docs, and readme for examples
1 parent eed6f9b commit 09030d4

File tree

2 files changed

+132
-136
lines changed

2 files changed

+132
-136
lines changed

docs/usage.md

Lines changed: 107 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22

33
This package works with media files in the [supported formats](https://github.com/contentauth/c2pa-rs/blob/main/docs/supported-formats.md).
44

5+
> **Note**: For complete working examples, see the [examples folder](https://github.com/contentauth/c2pa-python/tree/main/examples) in the repository.
6+
57
## Import
68

7-
Import the API as follows:
9+
Import the API:
810

911
```py
10-
from c2pa import *
12+
from c2pa import Builder, Reader, Signer, C2paSigningAlg, C2paSignerInfo
1113
```
1214

1315
## Define manifest JSON
1416

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

1820
```py
1921
manifest_json = json.dumps({
@@ -34,58 +36,33 @@ manifest_json = json.dumps({
3436
})
3537
```
3638

37-
## Signing function
38-
39-
The `sign_ps256` function is [defined in the library](https://github.com/contentauth/c2pa-python/blob/main/c2pa/c2pa_api/c2pa_api.py#L244) and used in both file-based and stream-based methods. It's reproduced here to show how signing is performed.
40-
41-
```py
42-
# Example of using Python crypto to sign data using openssl with Ps256
43-
from cryptography.hazmat.primitives import hashes, serialization
44-
from cryptography.hazmat.primitives.asymmetric import padding
45-
46-
def sign_ps256(data: bytes, key: bytes) -> bytes:
47-
private_key = serialization.load_pem_private_key(
48-
key,
49-
password=None,
50-
)
51-
signature = private_key.sign(
52-
data,
53-
padding.PSS(
54-
mgf=padding.MGF1(hashes.SHA256()),
55-
salt_length=padding.PSS.MAX_LENGTH
56-
),
57-
hashes.SHA256()
58-
)
59-
return signature
60-
```
61-
6239
## File-based operation
6340

6441
### Read and validate C2PA data
6542

66-
Use the `Reader` to read C2PA data from the specified asset file.
43+
Use the `Reader` to read C2PA data from the specified asset file.
6744

6845
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.
6946

70-
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`.
47+
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` using the associated `identifier` field values and a `uri`.
7148

7249
NOTE: For a comprehensive reference to the JSON manifest structure, see the [Manifest store reference](https://opensource.contentauthenticity.org/docs/manifest/manifest-ref).
7350

7451
```py
7552
try:
76-
# Create a reader from a file path
77-
reader = c2pa.Reader.from_file("path/to/media_file.jpg")
78-
79-
# Print the JSON for a manifest.
80-
print("Manifest store:", reader.json())
81-
82-
# Get the active manifest.
83-
manifest = reader.get_active_manifest()
84-
if manifest != None:
85-
86-
# get the uri to the manifest's thumbnail and write it to a file
87-
uri = manifest["thumbnail"]["identifier"]
88-
reader.resource_to_file(uri, "thumbnail_v2.jpg")
53+
# Create a reader from a file path
54+
with Reader("path/to/media_file.jpg") as reader:
55+
# Print the JSON for a manifest.
56+
print("Manifest store:", reader.json())
57+
58+
# Get the active manifest.
59+
manifest = json.loads(reader.json())
60+
active_manifest = manifest["manifests"][manifest["active_manifest"]]
61+
if active_manifest:
62+
# get the uri to the manifest's thumbnail and write it to a file
63+
uri = active_manifest["thumbnail"]["identifier"]
64+
with open("thumbnail_v2.jpg", "wb") as f:
65+
reader.resource_to_stream(uri, f)
8966

9067
except Exception as err:
9168
print(err)
@@ -98,76 +75,68 @@ except Exception as err:
9875
Use a `Builder` to add a manifest to an asset:
9976

10077
```py
101-
def test_v2_sign(self):
102-
# Define source folder for any assets being read.
103-
data_dir = "tests/fixtures/"
104-
try:
105-
key = open(data_dir + "ps256.pem", "rb").read()
106-
def sign(data: bytes) -> bytes:
107-
return sign_ps256(data, key)
108-
109-
certs = open(data_dir + "ps256.pub", "rb").read()
110-
# Create a local signer from a certificate pem file.
111-
signer = create_signer(sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com")
112-
113-
builder = Builder(manifest_def)
114-
115-
builder.add_ingredient_file(ingredient_def, data_dir + "A.jpg")
116-
117-
builder.add_resource_file("A.jpg", data_dir + "A.jpg")
118-
119-
builder.to_archive(open("target/archive.zip", "wb"))
120-
121-
builder = Builder.from_archive(open("target/archive.zip", "rb"))
122-
123-
with tempfile.TemporaryDirectory() as output_dir:
124-
output_path = output_dir + "out.jpg"
125-
if os.path.exists(output_path):
126-
os.remove(output_path)
127-
c2pa_data = builder.sign_file(signer, data_dir + "A.jpg", output_dir + "out.jpg")
128-
assert len(c2pa_data) > 0
129-
130-
reader = Reader.from_file(output_dir + "out.jpg")
131-
print(reader.json())
132-
manifest_store = json.loads(reader.json())
133-
manifest = manifest_store["manifests"][manifest_store["active_manifest"]]
134-
assert "python_test" in manifest["claim_generator"]
135-
# Check custom title and format.
136-
assert manifest["title"]== "My Title"
137-
assert manifest,["format"] == "image/jpeg"
138-
# There should be no validation status errors.
139-
assert manifest.get("validation_status") == None
140-
assert manifest["ingredients"][0]["relationship"] == "parentOf"
141-
assert manifest["ingredients"][0]["title"] == "A.jpg"
142-
143-
except Exception as e:
144-
print("Failed to sign manifest store: " + str(e))
145-
exit(1)
78+
try:
79+
# Create a signer from certificate and key files
80+
with open("path/to/cert.pem", "rb") as cert_file, open("path/to/key.pem", "rb") as key_file:
81+
cert_data = cert_file.read()
82+
key_data = key_file.read()
83+
84+
# Create signer info using cert and key info
85+
signer_info = C2paSignerInfo(
86+
alg=C2paSigningAlg.PS256,
87+
cert=cert_data,
88+
key=key_data,
89+
timestamp_url="http://timestamp.digicert.com"
90+
)
91+
92+
# Create signer using the defined SignerInfo
93+
signer = Signer.from_info(signer_info)
94+
95+
# Create builder with manifest and add ingredients
96+
with Builder(manifest_json) as builder:
97+
# Add any ingredients if needed
98+
with open("path/to/ingredient.jpg", "rb") as ingredient_file:
99+
ingredient_json = json.dumps({"title": "Ingredient Image"})
100+
builder.add_ingredient(ingredient_json, "image/jpeg", ingredient_file)
101+
102+
# Sign the file
103+
with open("path/to/source.jpg", "rb") as source_file, open("path/to/output.jpg", "wb") as dest_file:
104+
manifest_bytes = builder.sign(signer, "image/jpeg", source_file, dest_file)
105+
106+
# Verify the signed file
107+
with Reader("path/to/output.jpg") as reader:
108+
manifest_store = json.loads(reader.json())
109+
active_manifest = manifest_store["manifests"][manifest_store["active_manifest"]]
110+
print("Signed manifest:", active_manifest)
111+
112+
except Exception as e:
113+
print("Failed to sign manifest store: " + str(e))
146114
```
147115

148116
## Stream-based operation
149117

150118
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.
151119

152-
### Read and validate C2PA data
120+
### Read and validate C2PA data using streams
153121

154122
```py
155123
try:
156-
# It's also possible to create a reader from a format and stream
157-
# Note that these two readers are functionally equivalent
158-
stream = open("path/to/media_file.jpg", "rb")
159-
reader = c2pa.Reader("image/jpeg", stream)
160-
161-
# Print the JSON for a manifest.
162-
print("manifest store:", reader.json())
163-
164-
# Get the active manifest.
165-
manifest = reader.get_active_manifest()
166-
if manifest != None:
167-
168-
# get the uri to the manifest's thumbnail and write it to a file
169-
uri = manifest["thumbnail"]["identifier"]
170-
reader.resource_to_file(uri, "thumbnail_v2.jpg")
124+
# Create a reader from a format and stream
125+
with open("path/to/media_file.jpg", "rb") as stream:
126+
# First parameter can be mimetype or extension of the file
127+
# But in any case we need something to identify the file type
128+
with Reader("image/jpeg", stream) as reader:
129+
# Print the JSON for a manifest.
130+
print("manifest store:", reader.json())
131+
132+
# Get the active manifest.
133+
manifest = json.loads(reader.json())
134+
active_manifest = manifest["manifests"][manifest["active_manifest"]]
135+
if active_manifest:
136+
# get the uri to the manifest's thumbnail and write it to a file
137+
uri = active_manifest["thumbnail"]["identifier"]
138+
with open("thumbnail_v2.jpg", "wb") as f:
139+
reader.resource_to_stream(uri, f)
171140

172141
except Exception as err:
173142
print(err)
@@ -180,39 +149,41 @@ except Exception as err:
180149
Use a `Builder` to add a manifest to an asset:
181150

182151
```py
183-
from c2pa import Builder, Error, Reader, SigningAlg, create_signer, sdk_version, sign_ps256, version
184-
...
185-
data_dir = "tests/fixtures/"
186152
try:
187-
key = open(data_dir + "ps256.pem", "rb").read()
188-
def sign(data: bytes) -> bytes:
189-
return sign_ps256(data, key)
190-
191-
certs = open(data_dir + "ps256.pub", "rb").read()
192-
# Create a local signer from a certificate pem file
193-
signer = create_signer(sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com")
194-
195-
builder = Builder(manifest_def)
196-
197-
builder.add_ingredient_file(ingredient_def, data_dir + "A.jpg")
198-
builder.add_resource_file("A.jpg", data_dir + "A.jpg")
199-
builder.to_archive(open("target/archive.zip", "wb"))
200-
201-
builder = Builder.from_archive(open("target/archive.zip", "rb"))
202-
203-
with tempfile.TemporaryDirectory() as output_dir:
204-
output_path = output_dir + "out.jpg"
205-
if os.path.exists(output_path):
206-
os.remove(output_path)
207-
c2pa_data = builder.sign_file(signer, data_dir + "A.jpg", output_dir + "out.jpg")
208-
assert len(c2pa_data) > 0
209-
210-
reader = Reader.from_file(output_dir + "out.jpg")
211-
print(reader.json())
212-
manifest_store = json.loads(reader.json())
213-
manifest = manifest_store["manifests"][manifest_store["active_manifest"]]
153+
# Create a signer from certificate and key files
154+
with open("path/to/cert.pem", "rb") as cert_file, open("path/to/key.pem", "rb") as key_file:
155+
cert_data = cert_file.read()
156+
key_data = key_file.read()
157+
158+
# Create signer info
159+
signer_info = C2paSignerInfo(
160+
alg=C2paSigningAlg.PS256,
161+
cert=cert_data,
162+
key=key_data,
163+
timestamp_url="http://timestamp.digicert.com"
164+
)
165+
166+
# Create signer using the defined SignerInfo
167+
signer = Signer.from_info(signer_info)
168+
169+
# Create builder with manifest and add ingredients
170+
with Builder(manifest_json) as builder:
171+
# Add any ingredients if needed
172+
with open("path/to/ingredient.jpg", "rb") as ingredient_file:
173+
ingredient_json = json.dumps({"title": "Ingredient Image"})
174+
builder.add_ingredient(ingredient_json, "image/jpeg", ingredient_file)
175+
176+
# Sign using streams
177+
with open("path/to/source.jpg", "rb") as source_file, open("path/to/output.jpg", "wb") as dest_file:
178+
manifest_bytes = builder.sign(signer, "image/jpeg", source_file, dest_file)
179+
180+
# Verify the signed file
181+
with open("path/to/output.jpg", "rb") as stream:
182+
with Reader("image/jpeg", stream) as reader:
183+
manifest_store = json.loads(reader.json())
184+
active_manifest = manifest_store["manifests"][manifest_store["active_manifest"]]
185+
print("Signed manifest:", active_manifest)
214186

215187
except Exception as e:
216188
print("Failed to sign manifest store: " + str(e))
217-
exit(1)
218-
```
189+
```

examples/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Usage examples
2+
3+
## Adding a "Do Not Train" Assertion
4+
5+
The `examples/training.py` script demonstrates how to add a "Do Not Train" assertion to an asset and verify it.
6+
7+
### Signing and Verifying Assets
8+
9+
The `examples/sign.py` script shows how to sign an asset with a C2PA manifest and verify it.
10+
11+
## Running the Examples
12+
13+
To run the examples, make sure you have the c2pa-python package installed and you're in the root directory of the project. We recommend working using virtual environments (venv).
14+
15+
Then you can run the examples with the following commands:
16+
17+
```bash
18+
# Run the "Do Not Train" assertion example
19+
python examples/training.py
20+
21+
# Run the signing and verification example
22+
python examples/sign.py
23+
```
24+
25+
The examples will use test files from the `tests/fixtures` directory and output the results to the `output` directory. Read manifest store data will be shown in the console you run the examples from.

0 commit comments

Comments
 (0)