#[cfg(feature = "file_io")]
use std::{
io::{Cursor, Read, Seek, Write},
path::{Path, PathBuf},
};
#[cfg(feature = "file_io")]
use c2pa::crypto::raw_signature::SigningAlg;
#[cfg(feature = "file_io")]
use c2pa::{
assertions::{
c2pa_action, labels::*, Action, Actions, CreativeWork, DataHash, Exif, SchemaDotOrgPerson,
},
create_signer, hash_stream_by_alg, Builder, ClaimGeneratorInfo, HashRange, Ingredient, Reader,
Relationship, Result,
};
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
println!("DataHash demo");
#[cfg(feature = "file_io")]
user_data_hash_with_sdk_hashing()?;
println!("Done with SDK hashing1");
#[cfg(feature = "file_io")]
user_data_hash_with_user_hashing()?;
println!("Done with SDK hashing2");
Ok(())
}
#[cfg(feature = "file_io")]
fn builder_from_source<S: AsRef<Path>>(source: S) -> Result<Builder> {
let mut parent = Ingredient::from_file(source.as_ref())?;
parent.set_relationship(Relationship::ParentOf);
let actions = Actions::new().add_action(
Action::new(c2pa_action::PLACED)
.set_parameter("ingredients", [parent.instance_id().to_owned()])?,
);
let creative_work =
CreativeWork::new().add_author(SchemaDotOrgPerson::new().set_name("me")?)?;
let exif = Exif::from_json_str(
r#"{
"@context" : {
"exif": "http://ns.adobe.com/exif/1.0/"
},
"exif:GPSVersionID": "2.2.0.0",
"exif:GPSLatitude": "39,21.102N",
"exif:GPSLongitude": "74,26.5737W",
"exif:GPSAltitudeRef": 0,
"exif:GPSAltitude": "100963/29890",
"exif:GPSTimeStamp": "2019-09-22T18:22:57Z"
}"#,
)?;
let mut builder = Builder::default();
let mut claim_generator = ClaimGeneratorInfo::new("test_app".to_string());
claim_generator.set_version("0.1");
builder
.set_claim_generator_info(claim_generator)
.add_ingredient(parent)
.add_assertion(ACTIONS, &actions)?
.add_assertion_json(CREATIVE_WORK, &creative_work)?
.add_assertion_json(EXIF, &exif)?;
Ok(builder)
}
#[cfg(feature = "file_io")]
fn user_data_hash_with_sdk_hashing() -> Result<()> {
let signcert_path = "sdk/tests/fixtures/certs/es256.pub";
let pkey_path = "sdk/tests/fixtures/certs/es256.pem";
let signer = create_signer::from_files(signcert_path, pkey_path, SigningAlg::Es256, None)?;
let src = "sdk/tests/fixtures/earth_apollo17.jpg";
let source = PathBuf::from(src);
let mut builder = builder_from_source(&source)?;
let placeholder_manifest =
builder.data_hashed_placeholder(signer.reserve_size(), "image/jpeg")?;
let bytes = std::fs::read(&source)?;
let mut output: Vec<u8> = Vec::with_capacity(bytes.len() + placeholder_manifest.len());
let manifest_pos = 2;
output.extend_from_slice(&bytes[0..manifest_pos]);
output.extend_from_slice(&placeholder_manifest);
output.extend_from_slice(&bytes[manifest_pos..]);
let mut output_stream = Cursor::new(output);
let mut dh = DataHash::new("my_manifest", "sha265");
let hr = HashRange::new(manifest_pos as u64, placeholder_manifest.len() as u64);
dh.add_exclusion(hr.clone());
let hash = hash_stream_by_alg("sha256", &mut output_stream, Some([hr].to_vec()), true)?;
dh.set_hash(hash);
let final_manifest = builder.sign_data_hashed_embeddable(signer.as_ref(), &dh, "image/jpeg")?;
output_stream.seek(std::io::SeekFrom::Start(2))?;
output_stream.write_all(&final_manifest)?;
output_stream.rewind()?;
let reader = Reader::from_stream("image/jpeg", &mut output_stream)?;
println!("{reader}\n");
Ok(())
}
#[cfg(feature = "file_io")]
fn user_data_hash_with_user_hashing() -> Result<()> {
let signcert_path = "sdk/tests/fixtures/certs/es256.pub";
let pkey_path = "sdk/tests/fixtures/certs/es256.pem";
let signer = create_signer::from_files(signcert_path, pkey_path, SigningAlg::Es256, None)?;
let src = "sdk/tests/fixtures/earth_apollo17.jpg";
let dst = "target/tmp/output_hashed.jpg";
let source = PathBuf::from(src);
let dest = PathBuf::from(dst);
let mut input_file = std::fs::OpenOptions::new().read(true).open(&source)?;
let mut output_file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(dest)?;
let mut builder = builder_from_source(&source)?;
let placeholder_manifest =
builder.data_hashed_placeholder(signer.reserve_size(), "image/jpeg")?;
let mut dh = DataHash::new("my_manifest", "sha265");
let hr = HashRange::new(2, placeholder_manifest.len() as u64);
dh.add_exclusion(hr);
let hash = hash_stream_by_alg("sha256", &mut input_file, None, true)?;
dh.set_hash(hash);
let final_manifest: Vec<u8> =
builder.sign_data_hashed_embeddable(signer.as_ref(), &dh, "image/jpeg")?;
input_file.rewind().unwrap();
let mut before = vec![0u8; 2];
input_file.read_exact(before.as_mut_slice()).unwrap();
output_file.write_all(&before).unwrap();
output_file.write_all(&final_manifest).unwrap();
let mut after_buf = Vec::new();
input_file.read_to_end(&mut after_buf).unwrap();
output_file.write_all(&after_buf).unwrap();
output_file.rewind()?;
let reader = Reader::from_stream("image/jpeg", output_file)?;
println!("{reader}\n");
Ok(())
}