Signing
Signing a media file is the process of adding a C2PA Manifest, with the accompanying Assertions, Claim, and Signature, to the metadata of that media file. In libc2pa
, this is done using the ClaimGenerator
class.
Instances of ClaimGenerator
are constructed from the ClaimGeneratorBuilder
class. The ClaimGeneratorBuilder
is prepared with information about the Claim and then used to "build" the ClaimGenerator
instance that will perform the actual Claim creation and signing operations.
Create a ClaimGeneratorBuilder
Instance
ClaimGeneratorBuilder
InstanceInstances of the ClaimGeneratorBuilder
are created with the C2PAFactory
:
#include "C2PA/C2PAFactory.hpp"
void main() {
// Create a ClaimGeneratorBuider
auto builder = c2pa::C2PAFactory::new_claim_generator_builder(
// no options specified yields default behavior
);
}
The returned instance is wrapped in a std::unique_ptr
, so it will be automatically destroyed when they go out of scope.
If you need the
ClaimGeneratorBuilder
to persist beyond the scope in which it was created, you can do so by using themove_unique_to_shared()
utility function to move it to astd::shared_ptr
. For example:#include "common/common_utils.hpp" auto cgb_shared = common_utils::move_unique_to_shared(cgb);
Customizing GenerationOptions
GenerationOptions
The ClaimGenerator
's behavior may optionally be customized with a GenerationOptions
structure, which is passed in when creating the ClaimGeneratorBuilder
:
auto cg_opt = c2pa::C2PAFactory::new_generation_options();
// Modify the options as desired before passing them to the "constructor".
auto builder = c2pa::C2PAFactory::new_claim_generator_builder(cg_opt);
The following sections describe the options that can be configured with this structure.
Specify a log location
The ClaimGenerator
will only write to a debug log if this value is set. For now, only file destinations are supported.
cg_opt->log_location = c2pa::C2PAFactory::new_file_asset("c2pa.log");
Set a prefix for custom assertions
By default, custom assertions will use the assertion_label
exactly as it's passed in to the add_custom_assertion()
. Setting this value to something other than an empty string will prepend a prefix to each custom assertion_label
.
cg_opt->custom_assertion_label_prefix = "com.truepic.";
Defining the Claim Generator information
The Claim Generator information is stored in the claim_generator_info
structure in the Claim. By default, this will be set to information describing the Truepic libc2pa library. That info can be overridden by setting this property.
cg_opt->claim_generator_info_json = R"(
{
"name": "My Claim Generator",
"version": "0.0.0",
"my_custom_property": "My custom value"
})";
Defining a Claim Generator icon
The Claim Generator icon is also stored in the Claim. There is no default icon. Set this property to specify the icon to use when creating the Claim.
cg_opt->claim_generator_icon = c2pa::C2PAFactory::new_copy_buffer_asset(cg_icon_png_data);
Forcing the selection of a hard binding assertion
By default, the ClaimGenerator
will choose the best hard binding assertion for an input file based on specification support, internal support, and industry support. However, this selection can be overridden by setting this property:
cg_opt->force_hash_type = common::HashAssertionType::Data;
Valid values are:
common::HashAssertionType::Boxes
common::HashAssertionType::Data
common::HashAssertionType::Auto
Forcing the generation of an external Manifest Store
The format (specifically the hard binding) of an embedded Manifest differs somewhat from that of an external Manifest. For file types that support an embedded Manifest Store, the ClaimGenerator
will always default to creating a Manifest that is appropriate for embedding in the media file. However, there are situations where it might be desirable to create an external Manifest Store even for file types that support embedding.
Unfortunately, due to the way the Manifest is compiled before it is signed, the ClaimGenerator
needs to know up-front whether it will be generating an embeddable or external Manifest. This information can be provided by setting this property:
cg_opt->force_external_manifest_store = true;
Setting this property to
true
is unnecessary for media types that don't currently support embedding a Manifest Store.When this property is set to
true
, the client must call thecreate_manifest_store()
method rather than thesign_media()
method. Calling the wrong method will throw an exception.
Disabling certificate validation
Before signing a Claim, the ClaimGenerator
will always try to perform validation on the provided X509 certificate. If this validation fails, then an exception will be thrown and signing will not proceed. This default behavior can be disabled (only in testing / debugging scenarios) by setting this property:
cg_opt->validate_certificate = false;
Specify a user-defined trust list
The user_trust_list
property allows the user to specify a set of trust roots that can be used (in addition to the built-in trust list) to establish trust in signer certificates. It is a list of ReadOnlyAssetPtr
objects, and so can contain any mixture of files and data buffers, each containing a certificate in either DER or PEM format. In the case of PEM data, it is acceptable to pass in a certificate chain as a single Asset
object.
This trust list will be used by the ClaimGenerator
to validate the signing certificate before the Claim is signed.
cv_opt->user_trust_list.push_back(
c2pa::C2PAFactory::new_read_only_file_asset("my_root_cert.pem"));
cv_opt->user_trust_list.push_back(
c2pa::C2PAFactory::new_copy_buffer_asset(other_root_cert_der_buffer));
cv_opt->user_trust_list.push_back(
c2pa::C2PAFactory::new_read_only_file_asset("a_cert_chain.pem"));
Specify the target specification version
The ClaimGenerator
is capable of following the rules from various versions of the C2PA specification. The target_spec_version
field instructs the ClaimGenerator
to use rules from a specific specification version when signing. Valid values are 1.3
, 1.4
, 2.0
, and 2.1
. The default value is 1.4
.
cg_opt->target_spec_version = "2.1";
Specify the minimum ingredient validation specification version
When ingredients are added to a signing operation, they are validated by the ClaimGenerator
before being included in the Manifest Store. The minimum_ingredient_spec_version
field specifies the minimum C2PA specification version that these ingredients must adhere to in order to be considered valid. Valid values are 1.0
, 1.1
, 1.2
, 1.3
, 1.4
, 2.0
, and 2.1
. The default value is 1.4
.
cg_opt->minimum_ingredient_spec_version = "1.0";
Customize the extra free
box size for BMFF signing
free
box size for BMFF signingWhen signing a BMFF file (for example, MP4) the ClaimGenerator
adds an extra free
box after the uuid
box containing the C2PA Manifest Store. This is to allow the Manifest Store to grow in the future without needing to modify the rest of the file. The size of this extra free
box can be customized by setting the auxiliary_bmff_buffer_size
field. The default value is 100000
(bytes).
cg_opt->auxiliary_bmff_buffer_size = 500000;
Chain Calls to the ClaimGeneratorBuilder
ClaimGeneratorBuilder
The methods on the ClaimGeneratorBuilder
support the Fluent design pattern. This means that they return a pointer to the ClaimGeneratorBuilder
instance so that the calls can be chained:
#include "C2PA/C2PAFactory.hpp"
void main() {
// Create a ClaimGeneratorBuilder
auto builder = c2pa::C2PAFactory::new_claim_generator_builder();
// Call methods on it
builder
// Provide claim information
->set_media_title("image.jpg")
->set_instance_id("3cc0ae0a-96df-4097-8d5f-ebc85dd1d996")
// Provide assertions
->add_assertion("metadata", exif_json)
->add_assertion("thumbnail", thumbnail_json)
// Provide signature information
->set_algorithm(common::SignatureAlgorithm::PS256)
->set_certificate_chain(certificate_chain_assets)
// Provide media data
->set_media_data(input_jpeg_asset);
// Build the ClaimGenerator
auto cg = builder->build();
}
Order of Operations
Calls can be made to the ClaimGeneratorBuilder
in any order, but the build()
call must be the last call made. Once the ClaimGenerator
is built, a timestamp must be added (if desired) before signing the Claim.
In general, this is a good order to follow when signing a claim:
- Create a
ClaimGeneratorBuilder
instance (required) - Provide claim information (optional)
- Provide assertions (optional)
- Provide signature information (required)
- Provide media data (required)
- Build the
ClaimGenerator
(required) - Provide a timestamp (optional)
- Perform the signature operation (required)
Provide Claim Information
This information will be placed into the Claim portion of the C2PA Manifest.
set_media_title()
set_media_title()
Optional
This is an optional step, but it is recommended that you set this value to something meaningful.
std::string media_title
- Can be set to any string, but is typically the filename of the input media file.
builder->set_media_title("image.jpg");
set_instance_id()
set_instance_id()
Optional
std::string instance_id
- Can be set to any string, but ideally should be a unique identifier, such as a GUID.
builder->set_instance_id("3cc0ae0a-96df-4097-8d5f-ebc85dd1d996");
Provide Assertions
Assertions are statements that the Claim Generator asserts are true about the media file, and the environment in which it was created. Any number of assertions can be added before the file is signed, and each will be placed in the Assertion Store portion of the C2PA Manifest.
As of v2.0 of the C2PA specification, assertions may be of two types:
- Created - Created assertions refer to a statement that the Claim Signer asserts to be true. Specified with the enumeration value
common::CREATED_ASSERTION
. - Gathered - Gathered assertions refer to information that the Claim Signer has gathered from other sources at Claim generation time. Specified with the enumeration value
common::GATHERED_ASSERTION
.
If the Claim Generator is creating a v1.x compliant Claim, then there is no distinction, but if a v2.0+ compliant Claim is desired, then the type of assertion should be specified when adding the assertion to the Claim.
add_assertion()
add_assertion()
Optional
This adds one of the "standard" assertions supported by libc2pa
to the Assertion Store. See Working With Assertions for more info on supported standard assertions. This is an optional step, and may be called any number of times to add multiple assertions.
std::string assertion_type
- An identifier denoting the type of assertion to add.std::string assertion_json_string
- A string containing a JSON structure describing the assertion to add.common::C2PAAssertionSource assertion_source
- Optional, Default: GATHERED_ASSERTION Defines wether the created assertion will be listed in thecreated_assertions
orgathered_assertions
field of a v2.0+ compliant Claim. This parameter is ignored when generating a v1.x compliant Claim.
builder->add_assertion(
"exif",
R"({
"tiff:Make": "Google",
"tiff:Model": "Pixel 7"
})",
common::GATHERED_ASSERTION
);
add_custom_assertion()
add_custom_assertion()
Optional
This adds a "custom" assertion (anything that isn't a supported "standard" assertion) to the Assertion Store. This is an optional step, and may be called any number of times to add multiple assertions.
std::string assertion_label
- Provides a label for the assertion. When placed in the Assertion Store, the final assertion label will be constructed using the provided value, following the template:<custom_assertion_label_prefix><assertion_label>
. (This can be modified by setting thecustom_assertion_label_prefix
in theGenerationOptions
structure.)std::string assertion_json_string
- A string containing the exact JSON to be placed in the assertion.bool convert_to_cbor
- Optional, Default: true Defines whether the created assertion will be of type CBOR or JSON.common::C2PAAssertionSource assertion_source
- Optional, Default: GATHERED_ASSERTION Defines wether the created assertion will be listed in thecreated_assertions
orgathered_assertions
field of a v2.0+ compliant Claim. This parameter is ignored when generating a v1.x compliant Claim.
builder->add_custom_assertion(
"my-cusom-assertion",
R"({
"prop1": "val1",
"prop2": "val2"
})",
common::GATHERED_ASSERTION
);
set_provided_thumbnail_assertion()
set_provided_thumbnail_assertion()
Optional
When adding a standard thumbnail
assertion, the libc2pa
code will automatically resize the media and generate the thumbnail image for you. However, in some situations it is preferable to generate the thumbnail with some other external process and provide it to the library to be included in the Manifest.
c2pa::ReadOnlyAssetPtr thumbnail_asset
- AnAsset
containing information about how to obtain the raw thumbnail image data (in JPEG, PNG, WebP, or AVIF format) to be stored in the assertion.common::C2PAAssertionSource assertion_source
- Optional, Default: GATHERED_ASSERTION Defines wether the created assertion will be listed in thecreated_assertions
orgathered_assertions
field of a v2.0+ compliant Claim. This parameter is ignored when generating a v1.x compliant Claim.
builder->set_provided_thumbnail_assertion(jpeg_thumbnail_asset);
Provide Signature Information
set_algorithm()
set_algorithm()
Required
This informs the ClaimGeneratorBuilder
of the cryptographic algorithm that will be used to generate the signature.
common::SignatureAlgorithm algorithm
- Valid values are:common::SignatureAlgorithm::ES256
- 256-bit elliptical curvecommon::SignatureAlgorithm::ES384
- 384-bit elliptical curvecommon::SignatureAlgorithm::ES512
- 512-bit elliptical curvecommon::SignatureAlgorithm::PS256
- 256-bit RSA-PSScommon::SignatureAlgorithm::PS384
- 384-bit RSA-PSScommon::SignatureAlgorithm::PS512
- 512-bit RSA-PSS
builder->set_algorithm(common::SignatureAlgorithm::ES256);
set_certificate_chain()
set_certificate_chain()
Required
This provides both the end-entity certificate that links the public key associated with the signing (private) key to the trust root.and any intermediate certificates needed to link that end-entity certificate to a trust root in the trust chain. The trust root should not be provided in this call.
std::vector<c2pa::ReadOnlyAssetPtr> certificate_chain
- An array of raw X509 certificates. It is a list ofReadOnlyAssetPtr
objects, and so can contain any mixture of files and data buffers, each containing a certificate in either DER or PEM format. In the case of PEM data, it is acceptable to pass in a certificate chain as a singleAsset
object. The order of the certificates isn't important. They will be sorted correctly before being placed into the Claim.
builder->set_certificate_chain({
c2pa::C2PAFactory::new_copy_buffer_asset(end_entity_cert_der),
c2pa::C2PAFactory::new_read_only_file_asset("intermediate_cert_chain.pem")
});
Provide Media Data
There are two types of media data that can be provided to the ClaimGeneratorBuilder
:
- Input media data (Required) - This is the media file that is to be signed. It must be one of the supported media types (JPEG, PNG, MP4, etc.)
- Ingredient media data (Optional) - This is one or more ingredient files used to compose the input media file.
Ingredients can be signed or unsigned. If there are no signed ingredients provided, then the Manifest Store in the resulting output media file will only contain a single C2PA Manifest. However, if at least one signed ingredient is provided, then the Manifest Store in the resulting output media file will contain a Manifest for each signed ingredient plus a new Active Manifest to represent the input media file. The Active Manifest will reference all ingredient media files with c2pa.ingredient.v2 assertions.
set_media_data()
set_media_data()
Required
This provides the input media.
c2pa::ReadWriteAssetPtr media
- The raw data of the input media file.
builder->set_media_data(c2pa::C2PAFactory::new_ref_buffer_asset(jpeg_buffer));
set_yuv_media_data()
set_yuv_media_data()
Optional
This is a method added specifically for customers that want to use a YUV workflow.
When working with YUV input, this method should be called instead of
set_media_data()
.
c2pa::ReadWriteAssetPtr media_data
- The raw YUV data of the input media file, in YUV data in NV12 format (4:2:0 subsampling, u / v are interleaved).int width
- The width of the full (Y) image.int height
- The height of the full (Y) image.int stride
- The number of bytes between scanlines in the full (Y) image.int quality
- The desired output JPEG compression level, in the range [1..100].
builder->set_yuv_media_data(
c2pa::C2PAFactory::new_ref_buffer_asset(yuv_buffer),
1280, 1024, 1320, 90);
add_ingredient()
add_ingredient()
Optional
This can be called as many times as desired to add ingredient files to the C2PA Claim.
c2pa::ReadOnlyAssetPtr ingredient
- The raw data of the ingredient media file.std::string title
- Optional For signed ingredients, the title will be pulled from the active Claim and this parameter will be ignored. For unsigned ingredients, this provides an opportunity to specify a title. If none is provided, then a random title will be generated.std::optional<common::IngredientRelationship>
- Optional, Since v5.0.0 Specifies the relationship between the input media asset and the provided ingredient asset. Valid values areParentOf
orComponentOf
. Only oneParentOf
ingredient may be specified. If not specified, then an appropriate value will be chosen automatically.
buider
->add_ingredient(
c2pa::C2PAFactory::new_ref_buffer_asset(jpeg_ingredient),
"jpeg_ingredient.jpg")
->add_ingredient(c2pa::C2PAFactory::new_read_only_file_asset("png_ingredient.png"));
add_ingredient_with_external_manifest_store()
add_ingredient_with_external_manifest_store()
Optional
This can be called as many times as desired to add ingredient files to the C2PA Claim. It allows the media file and an external Manifest Store to be passed in separately.
c2pa::ReadOnlyAssetPtr manifest_store
- The raw data of the external Manifest Store.c2pa::ReadOnlyAssetPtr ingredient
- The raw data of the ingredient media file.std::optional<common::IngredientRelationship>
- Optional, Since v5.0.0 Specifies the relationship between the input media asset and the provided ingredient asset. Valid values areParentOf
orComponentOf
. Only oneParentOf
ingredient may be specified. If not specified, then an appropriate value will be chosen automatically.
builder->add_ingredient_with_external_manifest_soter(
c2pa::C2PAFactory::new_ref_buffer_asset(manifest_store),
c2pa::C2PAFactory::new_read_only_file_asset("ingredient.jpg"));
Build the ClaimGenerator
ClaimGenerator
The ClaimGenerator
is created by calling build()
on the ClaimGeneratorBuilder
. This should be done only one time, after all of the claim attributes have been fully defined via calls to methods on the builder object. After a call to build()
, the ClaimGeneratorBuilder
instance is invalid and must be discarded.
build()
build()
Required
This returns an instance of ClaimGenerator
wrapped in a std::unique_ptr
.
auto cg = builder->build();
Provide a Timestamp
A timestamp from a TSA (Time Stamping Authority) may optionally be added to the C2PA Signature. This step (if performed) must be done before the call to get_to_be_signed()
The process for doing this is:
- Get a TSA request from the
ClaimGenerator
. - Send (POST) that TSA request to a TSA.
Truepic's TSA can be reached athttps://tsa.truepic.com/signserver/process?workerName=LensTSAWorker
- Provide the TSA response (the response to the POST request) to the
ClaimGenerator
.
The
libc2pa
library contains helper methods to assist with step 2, if desired. See libc2pa Utility Functions for more info.
get_tsa_request()
get_tsa_request()
Optional
Generates and returns a TSA request, as a std::vector<uint8_t>
, to be sent to the TSA. This function must be called when signing against a version of the C2PA specification below 2.1. If signing with 2.1 or above, use get_v2_tsa_request()
instead.
auto request = cg->get_tsa_request();
get_v2_tsa_request()
get_v2_tsa_request()
Optional
Since v5.0.0
Generates and returns a v2 TSA request, as a std::vector<uint8_t>
, to be sent to the TSA. This function must be called when signing against the 2.1 C2PA specification or above. If signing with 2.0 or below, use get_tsa_request()
instead.
std::vector<uint8_t> signature
- The signature produced when signing the TBS structure. This must be the same data passed in tosign_media()
.
auto request = cg->get_tsa_request();
set_tsa_response()
set_tsa_response()
Optional
Adds the response from the TSA to the C2PA Signature.
std::vector<uint8_t> response
- The response data returned from the TSA.
auto response = post_to_tsa(request);
cg->set_tsa_response(response);
Perform the Signature Operation
This is the final step of the signing process. It can only be done once, and after the signature operation is complete, the ClaimGenerator
instance is unusable. A new instance must be created to perform another signing operation.
The process for signing the claim is:
- Get the TBS structure (to-be-signed structure) from the
ClaimGenerator
. - Calculate a cryptographic signature over the TBS structure using the private key that matches the public key in the previously provided end-entity certificate.
- Provide the resulting signature back to the
ClaimGenerator
to be included in the C2PA Signature.
The
libc2pa
library contains helper functions to assist with step 2, if desired. See libc2pa Utility Functions for more info.
The signature of the method for signing the media differs depending on which type of ClaimGenerator
was created (buffer, stream, or file).
get_to_be_signed()
get_to_be_signed()
Required
Generates and returns a TBS structure, as a std::vector<uint8_t>
, to be cryptographically signed.
auto tbs_structure = cg->get_to_be_signed();
sign_media()
sign_media()
Required
Embeds the new C2PA Manifest Store (with all Manifests, Claims, Signatures, etc.) into the input media file. It returns the resulting output media file as a c2pa::ReadWriteAssetPtr
. The type of Asset
returned will match the type of Asset
passed in to the set_media_data()
method. If a buffer Asset
was provided, then a buffer will be returned. If a std::stream
was provided, then that type will be returned. If a file was provided, then a file Asset
will be returned. If the output is a buffer Asset
, then it will be a newly generated buffer, and the original data buffer will not have been modified. If the output is either a std::stream
or a file, then they will point to the original stream or file that was provided to the library, and that asset will have been modified in-place to embed the Manifest Store.
std::vector<uint8_t> signature
- The signature produced when signing the TBS structure.
auto output_asset = cg->sign_media(signature_buffer);
Updated about 1 month ago