-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathsign.py
More file actions
115 lines (101 loc) · 3.91 KB
/
sign.py
File metadata and controls
115 lines (101 loc) · 3.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# Copyright 2025 Adobe. All rights reserved.
# This file is licensed to you under the Apache License,
# Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
# or the MIT license (http://opensource.org/licenses/MIT),
# at your option.
# Unless required by applicable law or agreed to in writing,
# this software is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
# implied. See the LICENSE-MIT and LICENSE-APACHE files for the
# specific language governing permissions and limitations under
# each license.
# This example shows how to sign an image with a C2PA manifest
# using a callback signer and read the metadata added to the image.
import os
import c2pa
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
# Note: Builder, Reader, Signer, and Context support being used as context managers
# (with 'with' statements) to automatically clean up resources.
fixtures_dir = os.path.join(os.path.dirname(__file__), "../tests/fixtures/")
output_dir = os.path.join(os.path.dirname(__file__), "../output/")
# Ensure the output directory exists.
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print("c2pa version:")
version = c2pa.sdk_version()
print(version)
# Load certificates and private key (here from the test fixtures).
# This is OK for development, but in production you should use a
# secure way to load the certificates and private key.
with open(fixtures_dir + "es256_certs.pem", "rb") as cert_file:
certs = cert_file.read()
with open(fixtures_dir + "es256_private.key", "rb") as key_file:
key = key_file.read()
# Define a callback signer function.
def callback_signer_es256(data: bytes) -> bytes:
"""Callback function that signs data using ES256 algorithm."""
private_key = serialization.load_pem_private_key(
key,
password=None,
backend=default_backend()
)
signature = private_key.sign(
data,
ec.ECDSA(hashes.SHA256())
)
return signature
# Create a manifest definition as a dictionary.
# This manifest follows the V2 manifest format.
manifest_definition = {
"claim_generator_info": [{
"name": "python_example",
"version": "0.0.1",
}],
# Claims version 2 is the default, so the version
# number can be omitted.
# "claim_version": 2,
"format": "image/jpeg",
"title": "Python Example Image",
"ingredients": [],
"assertions": [
{
"label": "c2pa.actions",
"data": {
"actions": [
{
"action": "c2pa.created",
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation"
}
]
}
}
]
}
# Sign the image with the signer created above,
# which will use the callback signer.
print("\nSigning the image file...")
# Use default Context and Settings.
with c2pa.Context() as context:
with c2pa.Signer.from_callback(
callback_signer_es256,
c2pa.C2paSigningAlg.ES256,
certs.decode('utf-8'),
"http://timestamp.digicert.com"
) as signer:
with c2pa.Builder(manifest_definition, context) as builder:
builder.sign_file(
fixtures_dir + "A.jpg",
output_dir + "A_signed.jpg",
signer
)
# Re-Read the signed image to verify
print("\nReading signed image metadata:")
with open(output_dir + "A_signed.jpg", "rb") as file:
with c2pa.Reader("image/jpeg", file, context=context) as reader:
# The validation state will depend on loaded trust settings.
# Without loaded trust settings,
# the manifest validation_state will be "Invalid".
print(reader.json())
print("\nExample completed successfully!")