Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ build-python:

# Performs a complete rebuild of the development environment
rebuild: clean-c2pa-env install-deps download-native-artifacts build-python
@echo "Development rebuild done!"
@echo "Development rebuild done"

run-examples:
python3 ./examples/sign.py
Expand Down
52 changes: 52 additions & 0 deletions src/c2pa/c2pa.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ class C2paSigningAlg(enum.IntEnum):
ED25519 = 6


# Mapping from C2paSigningAlg enum to string representation,
# as the enum value currently maps by default to an integer value.
_ALG_TO_STRING_BYTES_MAPPING = {
C2paSigningAlg.ES256: b"es256",
C2paSigningAlg.ES384: b"es384",
C2paSigningAlg.ES512: b"es512",
C2paSigningAlg.PS256: b"ps256",
C2paSigningAlg.PS384: b"ps384",
C2paSigningAlg.PS512: b"ps512",
C2paSigningAlg.ED25519: b"ed25519",
}


# Define callback types
ReadCallback = ctypes.CFUNCTYPE(
ctypes.c_ssize_t,
Expand Down Expand Up @@ -205,6 +218,45 @@ class C2paSignerInfo(ctypes.Structure):
("ta_url", ctypes.c_char_p),
]

def __init__(self, alg, sign_cert, private_key, ta_url):
"""Initialize C2paSignerInfo with optional parameters.

Args:
alg: The signing algorithm, either as a C2paSigningAlg enum or string or bytes
(will be converted accordingly to bytes for native library use)
sign_cert: The signing certificate as a string
private_key: The private key as a string
ta_url: The timestamp authority URL as bytes
"""
# Handle alg parameter: can be C2paSigningAlg enum or string (or bytes), convert as needed
if isinstance(alg, C2paSigningAlg):
# Convert enum to string representation
alg_str = _ALG_TO_STRING_BYTES_MAPPING.get(alg)
if alg_str is None:
raise ValueError(f"Unsupported signing algorithm: {alg}")
alg = alg_str
elif isinstance(alg, str):
# String to bytes, as requested by native lib
alg = alg.encode('utf-8')
elif isinstance(alg, bytes):
# In bytes already
pass
else:
raise TypeError(f"alg must be C2paSigningAlg enum, string, or bytes, got {type(alg)}")

# Handle ta_url parameter: allow string or bytes, convert string to bytes as needed
if isinstance(ta_url, str):
# String to bytes, as requested by native lib
ta_url = ta_url.encode('utf-8')
elif isinstance(ta_url, bytes):
# In bytes already
pass
else:
raise TypeError(f"ta_url must be string or bytes, got {type(ta_url)}")

# Call parent constructor with processed values
super().__init__(alg, sign_cert, private_key, ta_url)


class C2paReader(ctypes.Structure):
"""Opaque structure for reader context."""
Expand Down
Binary file modified tests/fixtures/files-for-reading-tests/C_with_CAWG_data.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 63 additions & 1 deletion tests/test_unit_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,69 @@ def test_read_file(self):
self.assertIn("manifests", file_data)
self.assertIn(expected_manifest_id, file_data["manifests"])

def test_sign_file(self):
def test_sign_file_alg_as_enum(self):
"""Test signing a file with C2PA manifest."""
# Set up test paths
temp_data_dir = os.path.join(self.data_dir, "temp_data")
os.makedirs(temp_data_dir, exist_ok=True)
output_path = os.path.join(temp_data_dir, "signed_output.jpg")

# Load test certificates and key
with open(os.path.join(self.data_dir, "es256_certs.pem"), "rb") as cert_file:
certs = cert_file.read()
with open(os.path.join(self.data_dir, "es256_private.key"), "rb") as key_file:
key = key_file.read()

# Create signer info
signer_info = C2paSignerInfo(
alg=SigningAlg.ES256,
sign_cert=certs,
private_key=key,
ta_url=b"http://timestamp.digicert.com"
)

# Create a simple manifest
manifest = {
"claim_generator": "python_internals_test",
"claim_generator_info": [{
"name": "python_internals_test",
"version": "0.0.1",
}],
"format": "image/jpeg",
"title": "Python Test Signed Image",
"ingredients": [],
"assertions": [
{
"label": "c2pa.actions",
"data": {
"actions": [
{
"action": "c2pa.opened"
}
]
}
}
]
}

# Convert manifest to JSON string
manifest_json = json.dumps(manifest)

try:
# Sign the file
result_json = sign_file(
self.testPath,
output_path,
manifest_json,
signer_info
)

finally:
# Clean up
if os.path.exists(output_path):
os.remove(output_path)

def test_sign_file_alg_as_bytes(self):
"""Test signing a file with C2PA manifest."""
# Set up test paths
temp_data_dir = os.path.join(self.data_dir, "temp_data")
Expand Down
Loading