Skip to content

Commit

Permalink
Merge pull request #28 from contentauth/gpeacock/data_hashed_embeddable
Browse files Browse the repository at this point in the history
feat: Adds data_hashed_embeddable support.
  • Loading branch information
gpeacock authored Nov 15, 2024
2 parents b3c2e46 + db511bd commit 0f76081
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 19 deletions.
70 changes: 69 additions & 1 deletion include/c2pa.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ IMPORT extern int c2pa_builder_to_archive(struct C2paBuilder *builder_ptr, struc
*
* # Safety
* Reads from NULL-terminated C strings
* If c2pa_data_ptr is not NULL, the returned value MUST be released by calling c2pa_release_string
* If manifest_bytes_ptr is not NULL, the returned value MUST be released by calling c2pa_manifest_bytes_free
* and it is no longer valid after that call.
*/
IMPORT extern
Expand All @@ -501,6 +501,59 @@ int c2pa_builder_sign(struct C2paBuilder *builder_ptr,
*/
IMPORT extern void c2pa_manifest_bytes_free(const unsigned char *manifest_bytes_ptr);

/**
* Creates a hashed placeholder from a Builder.
* The placeholder is used to reserve size in an asset for later signing.
*
* # Parameters
* * builder_ptr: pointer to a Builder.
* * reserved_size: the size required for a signature from the intended signer.
* * format: pointer to a C string with the mime type or extension.
* * manifest_bytes_ptr: pointer to a pointer to a c_uchar to return manifest_bytes.
*
* # Errors
* Returns -1 if there were errors, otherwise returns the size of the manifest_bytes.
* The error string can be retrieved by calling c2pa_error.
*
* # Safety
* Reads from NULL-terminated C strings.
* If manifest_bytes_ptr is not NULL, the returned value MUST be released by calling c2pa_manifest_bytes_free
* and it is no longer valid after that call.
*/
IMPORT extern
int c2pa_builder_data_hashed_placeholder(struct C2paBuilder *builder_ptr,
uintptr_t reserved_size,
const char *format,
const unsigned char **manifest_bytes_ptr);

/**
* Sign a Builder using the specified signer and data hash.
* The data hash is a JSON string containing DataHash information for the asset.
* This is a low-level method for advanced use cases where the caller handles embedding the manifest.
*
* # Parameters
* * builder_ptr: pointer to a Builder.
* * signer: pointer to a C2paSigner.
* * data_hash: pointer to a C string with the JSON data hash.
* * format: pointer to a C string with the mime type or extension.
* * manifest_bytes_ptr: pointer to a pointer to a c_uchar to return manifest_bytes (optional, can be NULL).
*
* # Errors
* Returns -1 if there were errors, otherwise returns the size of the manifest_bytes.
* The error string can be retrieved by calling c2pa_error.
*
* # Safety
* Reads from NULL-terminated C strings.
* If manifest_bytes_ptr is not NULL, the returned value MUST be released by calling c2pa_manifest_bytes_free
* and it is no longer valid after that call.
*/
IMPORT extern
int c2pa_builder_sign_data_hashed_embeddable(struct C2paBuilder *builder_ptr,
struct C2paSigner *signer,
const char *data_hash,
const char *format,
const unsigned char **manifest_bytes_ptr);

/**
* Creates a C2paSigner from a callback and configuration.
*
Expand Down Expand Up @@ -534,6 +587,21 @@ struct C2paSigner *c2pa_signer_create(const void *context,
const char *certs,
const char *tsa_url);

/**
* Returns the size to reserve for the signature for this signer.
*
* # Parameters
* * signer_ptr: pointer to a C2paSigner.
*
* # Errors
* Returns -1 if there were errors, otherwise returns the size to reserve.
* The error string can be retrieved by calling c2pa_error.
*
* # Safety
* The signer_ptr must be a valid pointer to a C2paSigner.
*/
IMPORT extern int64_t c2pa_signer_reserve_size(struct C2paSigner *signer_ptr);

/**
* Frees a C2paSigner allocated by Rust.
*
Expand Down
21 changes: 20 additions & 1 deletion include/c2pa.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,13 @@ namespace c2pa
Signer(SignerFunc *callback, C2paSigningAlg alg, const string &sign_cert, const string &tsa_uri);

Signer(C2paSigner *signer) : signer(signer) {}

~Signer();

/// @brief Get the size to reserve for a signature for this signer.
/// @return Reserved size for the signature.
uintptr_t reserve_size();

/// @brief Get the C2paSigner
C2paSigner *c2pa_signer();
};
Expand Down Expand Up @@ -268,6 +272,21 @@ namespace c2pa
/// @throws C2pa::Exception for errors encountered by the C2PA library.
void to_archive(const path &dest_path);

/// @brief Create a hashed placeholder from the builder.
/// @param reserved_size The size required for a signature from the intended signer.
/// @param format The format of the mime type or extension.
/// @return A vector containing the hashed placeholder.
/// @throws C2pa::Exception for errors encountered by the C2PA library.
std::unique_ptr<std::vector<unsigned char>> data_hashed_placeholder(uintptr_t reserved_size, const string &format);

/// @brief Sign a Builder using the specified signer and data hash.
/// @param signer The signer to use for signing.
/// @param data_hash The data hash to sign.
/// @param format The format of the data hash.
/// @return A vector containing the signed data.
/// @throws C2pa::Exception for errors encountered by the C2PA library.
std::unique_ptr<std::vector<unsigned char>> sign_data_hashed_embeddable(Signer &signer, const string &data_hash, const string &format);

private:
// Private constructor for Builder from an archive (todo: find a better way to handle this)
Builder(istream &archive);
Expand Down
50 changes: 47 additions & 3 deletions src/c2pa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,12 @@ namespace c2pa
return signer;
}

/// @brief Get the size to reserve for a signature for this signer.
uintptr_t Signer::reserve_size()
{
return c2pa_signer_reserve_size(signer);
}

/// @brief Builder class for creating a manifest implementation.
Builder::Builder(const string &manifest_json)
{
Expand Down Expand Up @@ -600,7 +606,8 @@ namespace c2pa
c2pa_builder_free(builder);
}

void Builder::set_no_embed() {
void Builder::set_no_embed()
{
c2pa_builder_set_no_embed(builder);
}

Expand Down Expand Up @@ -662,7 +669,7 @@ namespace c2pa
{
CppIStream c_source = CppIStream(source);
CppOStream c_dest = CppOStream(dest);
const unsigned char *c2pa_manifest_bytes = NULL; // TODO: Make returning manifest bytes optional.
const unsigned char *c2pa_manifest_bytes = NULL;
auto result = c2pa_builder_sign(builder, format.c_str(), c_source.c_stream, c_dest.c_stream, signer.c2pa_signer(), &c2pa_manifest_bytes);
if (result < 0)
{
Expand Down Expand Up @@ -759,4 +766,41 @@ namespace c2pa
to_archive(dest);
}

}
std::unique_ptr<std::vector<unsigned char>> Builder::data_hashed_placeholder(uintptr_t reserve_size, const string &format)
{
const unsigned char *c2pa_manifest_bytes = NULL;
auto result = c2pa_builder_data_hashed_placeholder(builder, reserve_size, format.c_str(), &c2pa_manifest_bytes);
if (result < 0)
{
throw Exception();
}
if (c2pa_manifest_bytes != NULL)
{
// Allocate a new vector on the heap and fill it with the data.
auto data = std::make_unique<std::vector<unsigned char>>(c2pa_manifest_bytes, c2pa_manifest_bytes + result);

c2pa_manifest_bytes_free(c2pa_manifest_bytes);
return data;
}
throw(c2pa::Exception("Failed to create data hashed placeholder"));
}

std::unique_ptr<std::vector<unsigned char>> Builder::sign_data_hashed_embeddable(Signer &signer, const string &data_hash, const string &format)
{
const unsigned char *c2pa_manifest_bytes = NULL;
auto result = c2pa_builder_sign_data_hashed_embeddable(builder, signer.c2pa_signer(), data_hash.c_str(), format.c_str(), &c2pa_manifest_bytes);
if (result < 0)
{
throw Exception();
}
if (c2pa_manifest_bytes != NULL)
{
// Allocate a new vector on the heap and fill it with the data.
auto data = std::make_unique<std::vector<unsigned char>>(c2pa_manifest_bytes, c2pa_manifest_bytes + result);

c2pa_manifest_bytes_free(c2pa_manifest_bytes);
return data;
}
throw(c2pa::Exception("Failed to create data hashed placeholder"));
}
} // namespace c2pa
154 changes: 151 additions & 3 deletions src/c_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use std::{

// C has no namespace so we prefix things with C2PA to make them unique
use c2pa::{
settings::load_settings_from_str, Builder as C2paBuilder, CallbackSigner, Reader as C2paReader,
SigningAlg,
assertions::DataHash, settings::load_settings_from_str, Builder as C2paBuilder, CallbackSigner,
Reader as C2paReader, SigningAlg,
};

use crate::{
Expand Down Expand Up @@ -71,6 +71,28 @@ pub struct C2paSigner {
pub signer: Box<dyn c2pa::Signer>,
}

// Internal routine to test for null and return null error
#[macro_export]
macro_rules! null_check {
($ptr : expr) => {
if $ptr.is_null() {
Error::set_last(Error::NullParameter(stringify!($ptr).to_string()));
return std::ptr::null_mut();
}
};
}

// Internal test for null and return -1 error
#[macro_export]
macro_rules! null_check_int {
($ptr : expr) => {
if $ptr.is_null() {
Error::set_last(Error::NullParameter(stringify!($ptr).to_string()));
return -1;
}
};
}

// Internal routine to convert a *const c_char to a rust String or return a NULL error.
#[macro_export]
macro_rules! from_cstr_null_check {
Expand Down Expand Up @@ -679,7 +701,7 @@ pub unsafe extern "C" fn c2pa_builder_to_archive(
///
/// # Safety
/// Reads from NULL-terminated C strings
/// If c2pa_data_ptr is not NULL, the returned value MUST be released by calling c2pa_release_string
/// If manifest_bytes_ptr is not NULL, the returned value MUST be released by calling c2pa_manifest_bytes_free
/// and it is no longer valid after that call.
#[no_mangle]
pub unsafe extern "C" fn c2pa_builder_sign(
Expand Down Expand Up @@ -730,6 +752,109 @@ pub unsafe extern "C" fn c2pa_manifest_bytes_free(manifest_bytes_ptr: *const c_u
}
}

/// Creates a hashed placeholder from a Builder.
/// The placeholder is used to reserve size in an asset for later signing.
///
/// # Parameters
/// * builder_ptr: pointer to a Builder.
/// * reserved_size: the size required for a signature from the intended signer.
/// * format: pointer to a C string with the mime type or extension.
/// * manifest_bytes_ptr: pointer to a pointer to a c_uchar to return manifest_bytes.
///
/// # Errors
/// Returns -1 if there were errors, otherwise returns the size of the manifest_bytes.
/// The error string can be retrieved by calling c2pa_error.
///
/// # Safety
/// Reads from NULL-terminated C strings.
/// If manifest_bytes_ptr is not NULL, the returned value MUST be released by calling c2pa_manifest_bytes_free
/// and it is no longer valid after that call.
#[no_mangle]
pub unsafe extern "C" fn c2pa_builder_data_hashed_placeholder(
builder_ptr: *mut C2paBuilder,
reserved_size: usize,
format: *const c_char,
manifest_bytes_ptr: *mut *const c_uchar,
) -> c_int {
null_check_int!(builder_ptr);
null_check_int!(manifest_bytes_ptr);
let mut builder: Box<C2paBuilder> = Box::from_raw(builder_ptr);
let format = from_cstr_null_check_int!(format);
let result = builder.data_hashed_placeholder(reserved_size, &format);
let _ = Box::into_raw(builder);
match result {
Ok(manifest_bytes) => {
let len = manifest_bytes.len() as c_int;
*manifest_bytes_ptr =
Box::into_raw(manifest_bytes.into_boxed_slice()) as *const c_uchar;
len
}
Err(err) => {
Error::from_c2pa_error(err).set_last();
-1
}
}
}

/// Sign a Builder using the specified signer and data hash.
/// The data hash is a JSON string containing DataHash information for the asset.
/// This is a low-level method for advanced use cases where the caller handles embedding the manifest.
///
/// # Parameters
/// * builder_ptr: pointer to a Builder.
/// * signer: pointer to a C2paSigner.
/// * data_hash: pointer to a C string with the JSON data hash.
/// * format: pointer to a C string with the mime type or extension.
/// * manifest_bytes_ptr: pointer to a pointer to a c_uchar to return manifest_bytes (optional, can be NULL).
///
/// # Errors
/// Returns -1 if there were errors, otherwise returns the size of the manifest_bytes.
/// The error string can be retrieved by calling c2pa_error.
///
/// # Safety
/// Reads from NULL-terminated C strings.
/// If manifest_bytes_ptr is not NULL, the returned value MUST be released by calling c2pa_manifest_bytes_free
/// and it is no longer valid after that call.
#[no_mangle]
pub unsafe extern "C" fn c2pa_builder_sign_data_hashed_embeddable(
builder_ptr: *mut C2paBuilder,
signer: *mut C2paSigner,
data_hash: *const c_char,
format: *const c_char,
manifest_bytes_ptr: *mut *const c_uchar,
) -> c_int {
null_check_int!(builder_ptr);
null_check_int!(manifest_bytes_ptr);

let mut builder: Box<C2paBuilder> = Box::from_raw(builder_ptr);
let c2pa_signer = Box::from_raw(signer);
let data_hash_json = from_cstr_null_check_int!(data_hash);
let data_hash: DataHash = match serde_json::from_str(&data_hash_json) {
Ok(data_hash) => data_hash,
Err(err) => {
Error::from_c2pa_error(c2pa::Error::JsonError(err)).set_last();
return -1;
}
};
let format = from_cstr_null_check_int!(format);
let result =
builder.sign_data_hashed_embeddable(c2pa_signer.signer.as_ref(), &data_hash, &format);
let _ = Box::into_raw(c2pa_signer);
let _ = Box::into_raw(builder);
match result {
Ok(manifest_bytes) => {
let len = manifest_bytes.len() as c_int;
*manifest_bytes_ptr =
Box::into_raw(manifest_bytes.into_boxed_slice()) as *const c_uchar;
len
}
Err(err) => {
Error::from_c2pa_error(err).set_last();
-1
}
}
}

/// Creates a C2paSigner from a callback and configuration.
///
/// # Parameters
Expand Down Expand Up @@ -796,6 +921,29 @@ pub unsafe extern "C" fn c2pa_signer_create(
}))
}

/// Returns the size to reserve for the signature for this signer.
///
/// # Parameters
/// * signer_ptr: pointer to a C2paSigner.
///
/// # Errors
/// Returns -1 if there were errors, otherwise returns the size to reserve.
/// The error string can be retrieved by calling c2pa_error.
///
/// # Safety
/// The signer_ptr must be a valid pointer to a C2paSigner.
#[no_mangle]
pub unsafe extern "C" fn c2pa_signer_reserve_size(signer_ptr: *mut C2paSigner) -> i64 {
if signer_ptr.is_null() {
Error::set_last(Error::NullParameter(stringify!($ptr).to_string()));
return -1;
}
let c2pa_signer: Box<C2paSigner> = Box::from_raw(signer_ptr);
let size = c2pa_signer.signer.reserve_size() as i64;
let _ = Box::into_raw(c2pa_signer);
size
}

/// Frees a C2paSigner allocated by Rust.
///
/// # Safety
Expand Down
Loading

0 comments on commit 0f76081

Please sign in to comment.