Skip to main content

Build the manifest

Before signing an image, you are required to generate a minimal C2PA manifest and compute its hash.

Required inputs

  • Image file path - Used as the title and for hash computation.    

  • Author name - Entered manually.   

  • Author email - Entered manually.

Both author name and email are typically fetched from your user certificate using the DigiCert credentials info API.

Process

  1. Compute the SHA256 hash of the input image (base64-encoded).

  2. Build a minimal manifest that includes:

    1. A unique manifest UUID and instance ID.

    2. Basic metadata such as title, format, and author details.

    3. Assertions describing the image hash and creative work metadata.

      1. Save the manifest to a local file (manifest.json).

      2. Pretty-print the manifest JSON to the console.

      3. Compute and display the SHA256 hash of the manifest (base64-encoded).

Output

  • A manifest.json file containing the to-be-signed manifest.    

  • The SHA256 hash of the manifest string to be used for signing.

Python example

# This Python script generates a minimal C2PA manifest for a given image, saves it as #...manifest.json, prints the manifest then computes and displays its SHA256 hash in base64.      

import json  import hashlib  
import base64  from datetime 
import datetime  
import uuid    

def compute_sha256(file_path):      
"""Compute SHA256 hash of a file and return base64-encoded string"""      
sha256 = hashlib.sha256()      
with open(file_path, "rb") as f:          
while chunk := f.read(8192):              
sha256.update(chunk)      
return base64.b64encode(sha256.digest()).decode('utf-8')  

def compute_sha256_of_string(input_str):      
"""Compute SHA256 hash of a string and return base64-encoded string"""    
sha256 = hashlib.sha256()      
sha256.update(input_str.encode('utf-8'))      
return base64.b64encode(sha256.digest()).decode('utf-8')    

def main():      
print("=== Minimal C2PA Manifest Generator ===\n")            

# Ask for required inputs      
image_path = input("Enter path to the image file: ").strip()      
author_name = input("Enter author name: ").strip()      
author_email = input("Enter author email: ").strip()  

# Generate UUIDs for manifest and instance      
manifest_uuid = str(uuid.uuid4())      
instance_id = f"xmp:iid:{uuid.uuid4()}"            

# Compute SHA256 hash of the image      
image_hash = compute_sha256(image_path)            

# Build minimal assertions      
assertions = [          
{              
"label": "c2pa.hash.data",              
"kind": "Json",              
"data": {                  
"alg": "sha256",                  
"hash": image_hash,                  
"name": "jumbf manifest",                  
"pad": ""              
}          
}, 
 {              
"label": "stds.schema-org.CreativeWork",              
"kind": "Json",              
"data": {                  
"@context": "https://schema.org",                  
"@type": "CreativeWork",                  
"author": [                      
{                          
"@type": "Person",                          
"name": author_name,                          
"email": author_email                      
}                  
],                  
"dateCreated": datetime.utcnow().isoformat() + "Z",                  
"name": image_path              
}          
}      
]  

# Build manifest     
manifest = {         
"active_manifest": f"urn:uuid:{manifest_uuid}",          
"manifests": {              
f"urn:uuid:{manifest_uuid}": {                  
"claim_generator": "Custom C2PA Manifest Generator",                  
"title": image_path,                  
"format": "image/jpeg",                  
"instance_id": instance_id,                  
"ingredients": [],                  
"assertions": assertions,                  
"label": f"urn:uuid:{manifest_uuid}"              
}          
},          
"validation_status": []      
}   

# Convert manifest to pretty JSON string      
manifest_json_str = json.dumps(manifest, indent=2)      
manifest_file_path = "manifest.json"      
with open(manifest_file_path, "w") as f:          
f.write(manifest_json_str)            

# Print manifest      
print("\n=== Generated To-Be-Signed Manifest ===\n")      
print(manifest_json_str)            

# Compute SHA256 hash of the manifest itself      
manifest_hash = compute_sha256_of_string(manifest_json_str)      
print("\n=== SHA256 Hash of the Manifest (base64) ===\n")      
print(manifest_hash)    

if __name__ == "__main__":      
main()