c2pa-python 0.22.0__tar.gz → 0.23.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {c2pa_python-0.22.0/src/c2pa_python.egg-info → c2pa_python-0.23.1}/PKG-INFO +6 -1
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/README.md +5 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/pyproject.toml +1 -1
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa/build.py +3 -4
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa/c2pa.py +141 -119
- {c2pa_python-0.22.0 → c2pa_python-0.23.1/src/c2pa_python.egg-info}/PKG-INFO +6 -1
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/tests/test_unit_tests.py +2 -2
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/LICENSE-APACHE +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/LICENSE-MIT +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/MANIFEST.in +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/requirements.txt +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/scripts/download_artifacts.py +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/setup.cfg +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/setup.py +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa/__init__.py +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa/lib.py +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa_python.egg-info/SOURCES.txt +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa_python.egg-info/dependency_links.txt +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa_python.egg-info/entry_points.txt +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa_python.egg-info/requires.txt +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/src/c2pa_python.egg-info/top_level.txt +0 -0
- {c2pa_python-0.22.0 → c2pa_python-0.23.1}/tests/test_unit_tests_threaded.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c2pa-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.23.1
|
|
4
4
|
Summary: Python bindings for the C2PA Content Authenticity Initiative (CAI) library
|
|
5
5
|
Author-email: Gavin Peacock <gvnpeacock@adobe.com>, Tania Mathern <mathern@adobe.com>
|
|
6
6
|
Maintainer-email: Gavin Peacock <gpeacock@adobe.com>
|
|
@@ -56,9 +56,14 @@ import c2pa
|
|
|
56
56
|
## Examples
|
|
57
57
|
|
|
58
58
|
See the [`examples` directory](https://github.com/contentauth/c2pa-python/tree/main/examples) for some helpful examples:
|
|
59
|
+
|
|
59
60
|
- `examples/sign.py` shows how to sign and verify an asset with a C2PA manifest.
|
|
60
61
|
- `examples/training.py` demonstrates how to add a "Do Not Train" assertion to an asset and verify it.
|
|
61
62
|
|
|
63
|
+
## API reference documentation
|
|
64
|
+
|
|
65
|
+
See [the section in Contributing to the project](https://github.com/contentauth/c2pa-python/blob/main/docs/project-contributions.md#api-reference-documentation).
|
|
66
|
+
|
|
62
67
|
## Contributing
|
|
63
68
|
|
|
64
69
|
Contributions are welcome! For more information, see [Contributing to the project](https://github.com/contentauth/c2pa-python/blob/main/docs/project-contributions.md).
|
|
@@ -33,9 +33,14 @@ import c2pa
|
|
|
33
33
|
## Examples
|
|
34
34
|
|
|
35
35
|
See the [`examples` directory](https://github.com/contentauth/c2pa-python/tree/main/examples) for some helpful examples:
|
|
36
|
+
|
|
36
37
|
- `examples/sign.py` shows how to sign and verify an asset with a C2PA manifest.
|
|
37
38
|
- `examples/training.py` demonstrates how to add a "Do Not Train" assertion to an asset and verify it.
|
|
38
39
|
|
|
40
|
+
## API reference documentation
|
|
41
|
+
|
|
42
|
+
See [the section in Contributing to the project](https://github.com/contentauth/c2pa-python/blob/main/docs/project-contributions.md#api-reference-documentation).
|
|
43
|
+
|
|
39
44
|
## Contributing
|
|
40
45
|
|
|
41
46
|
Contributions are welcome! For more information, see [Contributing to the project](https://github.com/contentauth/c2pa-python/blob/main/docs/project-contributions.md).
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "c2pa-python"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.23.1"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
description = "Python bindings for the C2PA Content Authenticity Initiative (CAI) library"
|
|
10
10
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import os
|
|
15
15
|
import sys
|
|
16
|
-
import requests
|
|
16
|
+
import requests # type: ignore
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
import zipfile
|
|
19
19
|
import io
|
|
@@ -53,8 +53,7 @@ def download_artifact(url: str, platform_name: str) -> None:
|
|
|
53
53
|
# Extract all files to the platform directory
|
|
54
54
|
zip_ref.extractall(platform_dir)
|
|
55
55
|
|
|
56
|
-
print(f"Successfully downloaded and extracted artifacts for {
|
|
57
|
-
platform_name}")
|
|
56
|
+
print(f"Successfully downloaded and extracted artifacts for {platform_name}")
|
|
58
57
|
|
|
59
58
|
|
|
60
59
|
def download_artifacts() -> None:
|
|
@@ -95,7 +94,7 @@ def download_artifacts() -> None:
|
|
|
95
94
|
def inject_version():
|
|
96
95
|
"""Inject the version from pyproject.toml
|
|
97
96
|
into src/c2pa/__init__.py as __version__."""
|
|
98
|
-
import toml
|
|
97
|
+
import toml # type: ignore
|
|
99
98
|
pyproject_path = os.path.abspath(
|
|
100
99
|
os.path.join(
|
|
101
100
|
os.path.dirname(__file__),
|
|
@@ -23,6 +23,7 @@ from typing import Optional, Union, Callable, Any, overload
|
|
|
23
23
|
import io
|
|
24
24
|
from .lib import dynamically_load_library
|
|
25
25
|
import mimetypes
|
|
26
|
+
from itertools import count
|
|
26
27
|
|
|
27
28
|
# Create a module-specific logger
|
|
28
29
|
logger = logging.getLogger("c2pa")
|
|
@@ -567,7 +568,7 @@ def _convert_to_py_string(value) -> str:
|
|
|
567
568
|
# Only if we got a valid pointer with valid content
|
|
568
569
|
if ptr and ptr.value is not None:
|
|
569
570
|
try:
|
|
570
|
-
py_string = ptr.value.decode('utf-8', errors='
|
|
571
|
+
py_string = ptr.value.decode('utf-8', errors='strict')
|
|
571
572
|
except Exception:
|
|
572
573
|
py_string = ""
|
|
573
574
|
finally:
|
|
@@ -706,24 +707,14 @@ def _get_mime_type_from_path(path: Union[str, Path]) -> str:
|
|
|
706
707
|
|
|
707
708
|
def read_ingredient_file(
|
|
708
709
|
path: Union[str, Path], data_dir: Union[str, Path]) -> str:
|
|
709
|
-
"""Read a file as C2PA ingredient.
|
|
710
|
+
"""Read a file as C2PA ingredient (deprecated).
|
|
710
711
|
This creates the JSON string that would be used as the ingredient JSON.
|
|
711
712
|
|
|
712
713
|
.. deprecated:: 0.11.0
|
|
713
714
|
This function is deprecated and will be removed in a future version.
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
with Reader(path) as reader:
|
|
718
|
-
manifest_json = reader.json()
|
|
719
|
-
```
|
|
720
|
-
|
|
721
|
-
To add ingredients to a manifest, please use the Builder class.
|
|
722
|
-
Example:
|
|
723
|
-
```
|
|
724
|
-
with open(ingredient_file_path, 'rb') as f:
|
|
725
|
-
builder.add_ingredient(ingredient_json, "image/jpeg", f)
|
|
726
|
-
```
|
|
715
|
+
To read C2PA metadata, use the :class:`c2pa.c2pa.Reader` class.
|
|
716
|
+
To add ingredients to a manifest,
|
|
717
|
+
use :meth:`c2pa.c2pa.Builder.add_ingredient` instead.
|
|
727
718
|
|
|
728
719
|
Args:
|
|
729
720
|
path: Path to the file to read
|
|
@@ -766,16 +757,11 @@ def read_ingredient_file(
|
|
|
766
757
|
|
|
767
758
|
def read_file(path: Union[str, Path],
|
|
768
759
|
data_dir: Union[str, Path]) -> str:
|
|
769
|
-
"""Read a C2PA manifest from a file.
|
|
760
|
+
"""Read a C2PA manifest from a file (deprecated).
|
|
770
761
|
|
|
771
762
|
.. deprecated:: 0.10.0
|
|
772
763
|
This function is deprecated and will be removed in a future version.
|
|
773
|
-
|
|
774
|
-
Example:
|
|
775
|
-
```python
|
|
776
|
-
with Reader(path) as reader:
|
|
777
|
-
manifest_json = reader.json()
|
|
778
|
-
```
|
|
764
|
+
To read C2PA metadata, use the :class:`c2pa.c2pa.Reader` class.
|
|
779
765
|
|
|
780
766
|
Args:
|
|
781
767
|
path: Path to the file to read
|
|
@@ -845,7 +831,7 @@ def sign_file(
|
|
|
845
831
|
signer_or_info: Union[C2paSignerInfo, 'Signer'],
|
|
846
832
|
return_manifest_as_bytes: bool = False
|
|
847
833
|
) -> Union[str, bytes]:
|
|
848
|
-
"""Sign a file with a C2PA manifest.
|
|
834
|
+
"""Sign a file with a C2PA manifest (deprecated).
|
|
849
835
|
For now, this function is left here to provide a backwards-compatible API.
|
|
850
836
|
|
|
851
837
|
.. deprecated:: 0.13.0
|
|
@@ -926,16 +912,11 @@ def sign_file(
|
|
|
926
912
|
|
|
927
913
|
|
|
928
914
|
class Stream:
|
|
929
|
-
# Class-level counter for generating
|
|
930
|
-
# (useful for tracing streams usage in debug)
|
|
931
|
-
|
|
915
|
+
# Class-level somewhat atomic counter for generating
|
|
916
|
+
# unique stream IDs (useful for tracing streams usage in debug)
|
|
917
|
+
_stream_id_counter = count(start=0, step=1)
|
|
918
|
+
|
|
932
919
|
# Maximum value for a 32-bit signed integer (2^31 - 1)
|
|
933
|
-
# This prevents integer overflow which could cause:
|
|
934
|
-
# 1. Unexpected behavior in stream ID generation
|
|
935
|
-
# 2. Potential security issues if IDs wrap around
|
|
936
|
-
# 3. Memory issues if the number grows too large
|
|
937
|
-
# When this limit is reached, we reset to 0 since the timestamp component
|
|
938
|
-
# of the stream ID ensures uniqueness even after counter reset
|
|
939
920
|
_MAX_STREAM_ID = 2**31 - 1
|
|
940
921
|
|
|
941
922
|
# Class-level error messages to avoid multiple creation
|
|
@@ -973,10 +954,15 @@ class Stream:
|
|
|
973
954
|
self._stream = None
|
|
974
955
|
|
|
975
956
|
# Generate unique stream ID using object ID and counter
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
Stream.
|
|
957
|
+
stream_counter = next(Stream._stream_id_counter)
|
|
958
|
+
|
|
959
|
+
# Handle counter overflow by resetting the counter
|
|
960
|
+
if stream_counter >= Stream._MAX_STREAM_ID: # pragma: no cover
|
|
961
|
+
# Reset the counter to 0 and get the next value
|
|
962
|
+
Stream._stream_id_counter = count(start=0, step=1)
|
|
963
|
+
stream_counter = next(Stream._stream_id_counter)
|
|
964
|
+
|
|
965
|
+
self._stream_id = f"{id(self)}-{stream_counter}"
|
|
980
966
|
|
|
981
967
|
# Rest of the existing initialization code...
|
|
982
968
|
required_methods = ['read', 'write', 'seek', 'tell', 'flush']
|
|
@@ -1125,7 +1111,7 @@ class Stream:
|
|
|
1125
1111
|
|
|
1126
1112
|
# Create the stream
|
|
1127
1113
|
self._stream = _lib.c2pa_create_stream(
|
|
1128
|
-
None,
|
|
1114
|
+
None,
|
|
1129
1115
|
self._read_cb,
|
|
1130
1116
|
self._seek_cb,
|
|
1131
1117
|
self._write_cb,
|
|
@@ -1242,7 +1228,15 @@ class Stream:
|
|
|
1242
1228
|
|
|
1243
1229
|
|
|
1244
1230
|
class Reader:
|
|
1245
|
-
"""High-level wrapper for C2PA Reader operations.
|
|
1231
|
+
"""High-level wrapper for C2PA Reader operations.
|
|
1232
|
+
|
|
1233
|
+
Example:
|
|
1234
|
+
```
|
|
1235
|
+
with Reader("image/jpeg", output) as reader:
|
|
1236
|
+
manifest_json = reader.json()
|
|
1237
|
+
```
|
|
1238
|
+
Where `output` is either an in-memory stream or an opened file.
|
|
1239
|
+
"""
|
|
1246
1240
|
|
|
1247
1241
|
# Supported mimetypes cache
|
|
1248
1242
|
_supported_mime_types_cache = None
|
|
@@ -1374,34 +1368,32 @@ class Reader:
|
|
|
1374
1368
|
str(e)))
|
|
1375
1369
|
|
|
1376
1370
|
try:
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
self._own_stream = Stream(file)
|
|
1380
|
-
|
|
1381
|
-
self._reader = _lib.c2pa_reader_from_stream(
|
|
1382
|
-
mime_type_str,
|
|
1383
|
-
self._own_stream._stream
|
|
1384
|
-
)
|
|
1371
|
+
with open(path, 'rb') as file:
|
|
1372
|
+
self._own_stream = Stream(file)
|
|
1385
1373
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
error = _parse_operation_result_for_error(
|
|
1390
|
-
_lib.c2pa_error())
|
|
1391
|
-
if error:
|
|
1392
|
-
raise C2paError(error)
|
|
1393
|
-
raise C2paError(
|
|
1394
|
-
Reader._ERROR_MESSAGES['reader_error'].format(
|
|
1395
|
-
"Unknown error"
|
|
1396
|
-
)
|
|
1374
|
+
self._reader = _lib.c2pa_reader_from_stream(
|
|
1375
|
+
mime_type_str,
|
|
1376
|
+
self._own_stream._stream
|
|
1397
1377
|
)
|
|
1398
1378
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1379
|
+
if not self._reader:
|
|
1380
|
+
self._own_stream.close()
|
|
1381
|
+
error = _parse_operation_result_for_error(
|
|
1382
|
+
_lib.c2pa_error())
|
|
1383
|
+
if error:
|
|
1384
|
+
raise C2paError(error)
|
|
1385
|
+
raise C2paError(
|
|
1386
|
+
Reader._ERROR_MESSAGES['reader_error'].format(
|
|
1387
|
+
"Unknown error"
|
|
1388
|
+
)
|
|
1389
|
+
)
|
|
1401
1390
|
|
|
1402
|
-
|
|
1391
|
+
# Store the file to close it later
|
|
1392
|
+
self._backing_file = file
|
|
1393
|
+
self._initialized = True
|
|
1403
1394
|
|
|
1404
1395
|
except Exception as e:
|
|
1396
|
+
# File automatically closed by context manager
|
|
1405
1397
|
if self._own_stream:
|
|
1406
1398
|
self._own_stream.close()
|
|
1407
1399
|
if hasattr(self, '_backing_file') and self._backing_file:
|
|
@@ -1420,50 +1412,49 @@ class Reader:
|
|
|
1420
1412
|
f"Reader does not support {format_or_path}")
|
|
1421
1413
|
|
|
1422
1414
|
try:
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1415
|
+
with open(stream, 'rb') as file:
|
|
1416
|
+
self._own_stream = Stream(file)
|
|
1417
|
+
|
|
1418
|
+
format_str = str(format_or_path)
|
|
1419
|
+
format_bytes = format_str.encode('utf-8')
|
|
1420
|
+
|
|
1421
|
+
if manifest_data is None:
|
|
1422
|
+
self._reader = _lib.c2pa_reader_from_stream(
|
|
1423
|
+
format_bytes, self._own_stream._stream)
|
|
1424
|
+
else:
|
|
1425
|
+
if not isinstance(manifest_data, bytes):
|
|
1426
|
+
raise TypeError(
|
|
1427
|
+
Reader._ERROR_MESSAGES['manifest_error'])
|
|
1428
|
+
manifest_array = (
|
|
1429
|
+
ctypes.c_ubyte *
|
|
1430
|
+
len(manifest_data))(
|
|
1431
|
+
*
|
|
1432
|
+
manifest_data)
|
|
1433
|
+
self._reader = (
|
|
1434
|
+
_lib.c2pa_reader_from_manifest_data_and_stream(
|
|
1435
|
+
format_bytes,
|
|
1436
|
+
self._own_stream._stream,
|
|
1437
|
+
manifest_array,
|
|
1438
|
+
len(manifest_data),
|
|
1439
|
+
)
|
|
1447
1440
|
)
|
|
1448
|
-
)
|
|
1449
1441
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
raise C2paError(
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1442
|
+
if not self._reader:
|
|
1443
|
+
self._own_stream.close()
|
|
1444
|
+
error = _parse_operation_result_for_error(
|
|
1445
|
+
_lib.c2pa_error())
|
|
1446
|
+
if error:
|
|
1447
|
+
raise C2paError(error)
|
|
1448
|
+
raise C2paError(
|
|
1449
|
+
Reader._ERROR_MESSAGES['reader_error'].format(
|
|
1450
|
+
"Unknown error"
|
|
1451
|
+
)
|
|
1460
1452
|
)
|
|
1461
|
-
)
|
|
1462
1453
|
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
self._initialized = True
|
|
1454
|
+
self._backing_file = file
|
|
1455
|
+
self._initialized = True
|
|
1466
1456
|
except Exception as e:
|
|
1457
|
+
# File closed by context manager
|
|
1467
1458
|
if self._own_stream:
|
|
1468
1459
|
self._own_stream.close()
|
|
1469
1460
|
if hasattr(self, '_backing_file') and self._backing_file:
|
|
@@ -2309,6 +2300,12 @@ class Builder:
|
|
|
2309
2300
|
C2paError: If there was an error adding the ingredient
|
|
2310
2301
|
C2paError.Encoding: If the ingredient JSON contains
|
|
2311
2302
|
invalid UTF-8 characters
|
|
2303
|
+
|
|
2304
|
+
Example:
|
|
2305
|
+
```
|
|
2306
|
+
with open(ingredient_file_path, 'rb') as a_file:
|
|
2307
|
+
builder.add_ingredient(ingredient_json, "image/jpeg", a_file)
|
|
2308
|
+
```
|
|
2312
2309
|
"""
|
|
2313
2310
|
return self.add_ingredient_from_stream(ingredient_json, format, source)
|
|
2314
2311
|
|
|
@@ -2366,7 +2363,7 @@ class Builder:
|
|
|
2366
2363
|
ingredient_json: str,
|
|
2367
2364
|
format: str,
|
|
2368
2365
|
filepath: Union[str, Path]):
|
|
2369
|
-
"""Add an ingredient from a file path to the builder.
|
|
2366
|
+
"""Add an ingredient from a file path to the builder (deprecated).
|
|
2370
2367
|
This is a legacy method.
|
|
2371
2368
|
|
|
2372
2369
|
.. deprecated:: 0.13.0
|
|
@@ -2635,7 +2632,7 @@ def create_signer(
|
|
|
2635
2632
|
certs: str,
|
|
2636
2633
|
tsa_url: Optional[str] = None
|
|
2637
2634
|
) -> Signer:
|
|
2638
|
-
"""Create a signer from a callback function.
|
|
2635
|
+
"""Create a signer from a callback function (deprecated).
|
|
2639
2636
|
|
|
2640
2637
|
.. deprecated:: 0.11.0
|
|
2641
2638
|
This function is deprecated and will be removed in a future version.
|
|
@@ -2670,7 +2667,7 @@ def create_signer(
|
|
|
2670
2667
|
|
|
2671
2668
|
|
|
2672
2669
|
def create_signer_from_info(signer_info: C2paSignerInfo) -> Signer:
|
|
2673
|
-
"""Create a signer from signer information.
|
|
2670
|
+
"""Create a signer from signer information (deprecated).
|
|
2674
2671
|
|
|
2675
2672
|
.. deprecated:: 0.11.0
|
|
2676
2673
|
This function is deprecated and will be removed in a future version.
|
|
@@ -2713,28 +2710,53 @@ def ed25519_sign(data: bytes, private_key: str) -> bytes:
|
|
|
2713
2710
|
C2paError: If there was an error signing the data
|
|
2714
2711
|
C2paError.Encoding: If the private key contains invalid UTF-8 chars
|
|
2715
2712
|
"""
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
key_str = private_key.encode('utf-8')
|
|
2719
|
-
except UnicodeError as e:
|
|
2720
|
-
raise C2paError.Encoding(
|
|
2721
|
-
f"Invalid UTF-8 characters in private key: {str(e)}")
|
|
2713
|
+
if not data:
|
|
2714
|
+
raise C2paError("Data to sign cannot be empty")
|
|
2722
2715
|
|
|
2723
|
-
|
|
2716
|
+
if not private_key or not isinstance(private_key, str):
|
|
2717
|
+
raise C2paError("Private key must be a non-empty string")
|
|
2724
2718
|
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
raise C2paError(error)
|
|
2729
|
-
raise C2paError("Failed to sign data with Ed25519")
|
|
2719
|
+
# Create secure memory buffer for data
|
|
2720
|
+
data_array = None
|
|
2721
|
+
key_bytes = None
|
|
2730
2722
|
|
|
2731
2723
|
try:
|
|
2732
|
-
#
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
_lib.c2pa_signature_free(signature_ptr)
|
|
2724
|
+
# Create data array with size validation
|
|
2725
|
+
data_size = len(data)
|
|
2726
|
+
data_array = (ctypes.c_ubyte * data_size)(*data)
|
|
2736
2727
|
|
|
2737
|
-
|
|
2728
|
+
# Encode private key to bytes
|
|
2729
|
+
try:
|
|
2730
|
+
key_bytes = private_key.encode('utf-8')
|
|
2731
|
+
except UnicodeError as e:
|
|
2732
|
+
raise C2paError.Encoding(
|
|
2733
|
+
f"Invalid UTF-8 characters in private key: {str(e)}")
|
|
2734
|
+
|
|
2735
|
+
# Perform the signing operation
|
|
2736
|
+
signature_ptr = _lib.c2pa_ed25519_sign(
|
|
2737
|
+
data_array,
|
|
2738
|
+
data_size,
|
|
2739
|
+
key_bytes
|
|
2740
|
+
)
|
|
2741
|
+
|
|
2742
|
+
if not signature_ptr:
|
|
2743
|
+
error = _parse_operation_result_for_error(_lib.c2pa_error())
|
|
2744
|
+
if error:
|
|
2745
|
+
raise C2paError(error)
|
|
2746
|
+
raise C2paError("Failed to sign data with Ed25519")
|
|
2747
|
+
|
|
2748
|
+
try:
|
|
2749
|
+
# Ed25519 signatures are always 64 bytes
|
|
2750
|
+
signature = bytes(signature_ptr[:64])
|
|
2751
|
+
finally:
|
|
2752
|
+
_lib.c2pa_signature_free(signature_ptr)
|
|
2753
|
+
|
|
2754
|
+
return signature
|
|
2755
|
+
|
|
2756
|
+
finally:
|
|
2757
|
+
if key_bytes:
|
|
2758
|
+
ctypes.memset(key_bytes, 0, len(key_bytes))
|
|
2759
|
+
del key_bytes
|
|
2738
2760
|
|
|
2739
2761
|
|
|
2740
2762
|
__all__ = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: c2pa-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.23.1
|
|
4
4
|
Summary: Python bindings for the C2PA Content Authenticity Initiative (CAI) library
|
|
5
5
|
Author-email: Gavin Peacock <gvnpeacock@adobe.com>, Tania Mathern <mathern@adobe.com>
|
|
6
6
|
Maintainer-email: Gavin Peacock <gpeacock@adobe.com>
|
|
@@ -56,9 +56,14 @@ import c2pa
|
|
|
56
56
|
## Examples
|
|
57
57
|
|
|
58
58
|
See the [`examples` directory](https://github.com/contentauth/c2pa-python/tree/main/examples) for some helpful examples:
|
|
59
|
+
|
|
59
60
|
- `examples/sign.py` shows how to sign and verify an asset with a C2PA manifest.
|
|
60
61
|
- `examples/training.py` demonstrates how to add a "Do Not Train" assertion to an asset and verify it.
|
|
61
62
|
|
|
63
|
+
## API reference documentation
|
|
64
|
+
|
|
65
|
+
See [the section in Contributing to the project](https://github.com/contentauth/c2pa-python/blob/main/docs/project-contributions.md#api-reference-documentation).
|
|
66
|
+
|
|
62
67
|
## Contributing
|
|
63
68
|
|
|
64
69
|
Contributions are welcome! For more information, see [Contributing to the project](https://github.com/contentauth/c2pa-python/blob/main/docs/project-contributions.md).
|
|
@@ -40,7 +40,7 @@ ALTERNATIVE_INGREDIENT_TEST_FILE = os.path.join(FIXTURES_DIR, "cloud.jpg")
|
|
|
40
40
|
|
|
41
41
|
class TestC2paSdk(unittest.TestCase):
|
|
42
42
|
def test_sdk_version(self):
|
|
43
|
-
self.assertIn("0.
|
|
43
|
+
self.assertIn("0.65.1", sdk_version())
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
class TestReader(unittest.TestCase):
|
|
@@ -1124,7 +1124,7 @@ class TestBuilderWithSigner(unittest.TestCase):
|
|
|
1124
1124
|
|
|
1125
1125
|
builder.close()
|
|
1126
1126
|
|
|
1127
|
-
# Settings are
|
|
1127
|
+
# Settings are thread-local, so we reset to the default "true" here
|
|
1128
1128
|
load_settings('{"builder": { "thumbnail": {"enabled": true}}}')
|
|
1129
1129
|
|
|
1130
1130
|
def test_builder_sign_with_duplicate_ingredient(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|