@@ -36,19 +36,18 @@ manifest_json = json.dumps({
3636
3737## Signing function
3838
39- The ` sign_ps256 ` function is [ defined in the library] ( https://github.com/contentauth/c2pa-python/blob/main/c2pa/c2pa_api/c2pa_api.py#L209 ) is used in both file-based and stream-based methods and is reproduced here to show how signing is performed.
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.
4040
4141``` py
4242# Example of using Python crypto to sign data using openssl with Ps256
4343from cryptography.hazmat.primitives import hashes, serialization
4444from cryptography.hazmat.primitives.asymmetric import padding
4545
46- def sign_ps256 (data : bytes , key_path : str ) -> bytes :
47- with open (key_path, " rb" ) as key_file:
48- private_key = serialization.load_pem_private_key(
49- key_file.read(),
50- password = None ,
51- )
46+ def sign_ps256 (data : bytes , key : bytes ) -> bytes :
47+ private_key = serialization.load_pem_private_key(
48+ key,
49+ password = None ,
50+ )
5251 signature = private_key.sign(
5352 data,
5453 padding.PSS(
7877 reader = c2pa.Reader.from_file(" path/to/media_file.jpg" )
7978
8079 # Print the JSON for a manifest.
81- print (" manifest store:" , reader.json())
80+ print (" Manifest store:" , reader.json())
8281
8382 # Get the active manifest.
8483 manifest = reader.get_active_manifest()
@@ -99,52 +98,51 @@ except Exception as err:
9998Use a ` Builder ` to add a manifest to an asset:
10099
101100``` py
102- try :
103- # Define a function to sign the claim bytes
104- # In this case we are using a pre-defined sign_ps256 method, passing in our private cert
105- # Normally this cert would be kept safe in some other location
106- def private_sign (data : bytes ) -> bytes :
107- return sign_ps256(data, " tests/fixtures/ps256.pem" )
108-
109- # read our public certs into memory
110- certs = open (data_dir + " ps256.pub" , " rb" ).read()
111-
112- # Create a signer from the private signer, certs and a time stamp service url
113- signer = create_signer(private_sign, SigningAlg.PS256 , certs, " http://timestamp.digicert.com" )
114-
115- # Create a builder add a thumbnail resource and an ingredient file.
116- builder = Builder(manifest_json)
117-
118- # The uri provided here "thumbnail" must match an identifier in the manifest definition.
119- builder.add_resource_file(" thumbnail" , " tests/fixtures/A_thumbnail.jpg" )
120-
121- # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail
122- ingredient_json = {
123- " title" : " A.jpg" ,
124- " relationship" : " parentOf" , # "parentOf", "componentOf" or "inputTo"
125- " thumbnail" : {
126- " identifier" : " thumbnail" ,
127- " format" : " image/jpeg"
128- }
129- }
130-
131- # Add the ingredient to the builder loading information from a source file.
132- builder.add_ingredient_file(ingredient_json, " tests/fixtures/A.jpg" )
133-
134- # At this point we could archive or unarchive our Builder to continue later.
135- # In this example we use a bytearray for the archive stream.
136- # all ingredients and resources will be saved in the archive
137- archive = io.BytesIO(bytearray ())
138- builder.to_archive(archive)
139- archive.seek()
140- builder = builder.from_archive(archive)
141-
142- # Sign and add our manifest to a source file, writing it to an output file.
143- # This returns the binary manifest data that could be uploaded to cloud storage.
144- c2pa_data = builder.sign_file(signer, " tests/fixtures/A.jpg" , " target/out.jpg" )
145-
146- except Exception as err:
147- print (err)
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 )
148146```
149147
150148## Stream-based operation
@@ -182,54 +180,39 @@ except Exception as err:
182180Use a ` Builder ` to add a manifest to an asset:
183181
184182``` py
183+ from c2pa import Builder, Error, Reader, SigningAlg, create_signer, sdk_version, sign_ps256, version
184+ ...
185+ data_dir = " tests/fixtures/"
185186try :
186- # Define a function to sign the claim bytes
187- # In this case we are using a pre-defined sign_ps256 method, passing in our private cert
188- # Normally this cert would be kept safe in some other location
189- def private_sign (data : bytes ) -> bytes :
190- return sign_ps256(data, " tests/fixtures/ps256.pem" )
191-
192- # read our public certs into memory
193- certs = open (data_dir + " ps256.pub" , " rb" ).read()
194-
195- # Create a signer from the private signer, certs and a time stamp service url
196- signer = create_signer(private_sign, SigningAlg.PS256 , certs, " http://timestamp.digicert.com" )
197-
198- # Create a builder add a thumbnail resource and an ingredient file.
199- builder = Builder(manifest_json)
200-
201- # Add the resource from a stream
202- a_thumbnail_jpg_stream = open (" tests/fixtures/A_thumbnail.jpg" , " rb" )
203- builder.add_resource(" image/jpeg" , a_thumbnail_jpg_stream)
204-
205- # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail
206- ingredient_json = {
207- " title" : " A.jpg" ,
208- " relationship" : " parentOf" , # "parentOf", "componentOf" or "inputTo"
209- " thumbnail" : {
210- " identifier" : " thumbnail" ,
211- " format" : " image/jpeg"
212- }
213- }
214-
215- # Add the ingredient from a stream
216- a_jpg_stream = open (" tests/fixtures/A.jpg" , " rb" )
217- builder.add_ingredient(" image/jpeg" , a_jpg_stream)
218-
219- # At this point we could archive or unarchive our Builder to continue later.
220- # In this example we use a bytearray for the archive stream.
221- # all ingredients and resources will be saved in the archive
222- archive = io.BytesIO(bytearray ())
223- builder.to_archive(archive)
224- archive.seek()
225- builder = builder.from_archive(archive)
226-
227- # Sign the builder with a stream and output it to a stream
228- # This returns the binary manifest data that could be uploaded to cloud storage.
229- input_stream = open (" tests/fixtures/A.jpg" , " rb" )
230- output_stream = open (" target/out.jpg" , " wb" )
231- c2pa_data = builder.sign(signer, " image/jpeg" , input_stream, output_stream)
232-
233- except Exception as err:
234- print (err)
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" ]]
214+
215+ except Exception as e:
216+ print (" Failed to sign manifest store: " + str (e))
217+ exit (1 )
235218 ```
0 commit comments