Skip to content
Merged
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# Generated files
.docusaurus
.cache-loader

# External files loaded by fetch-readmes script
/docs/js-sdk/api
/docs/js-sdk/examples/quickstart/*.ts
/docs/js-sdk/examples/view-manifest/*.ts
Expand All @@ -23,6 +25,7 @@
/docs/rust-sdk/*.md
/docs/rust-sdk/docs/*.md
/docs/**/readme.md
/static/sb-alg-list.json

# Misc
.DS_Store
Expand Down
15 changes: 15 additions & 0 deletions docs/soft-bindings.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
id: sb-algs
title: Watermarking and fingerprinting algorithms
hide_table_of_contents: true
---

[_Durable Content Credentials_](https://contentauthenticity.org/blog/durable-content-credentials) is a concept that combines C2PA secure metadata, undetectable watermarks, and content fingerprinting to make content provenance more robust than using just one of these methods.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest expanded wording:
Durable Content Credentials is a concept that combines C2PA secure metadata with either, or both of, invisible watermarking, and content fingerprinting to help content provenance information persist across content platforms.

C2PA metadata can be accidentally removed when the image is shared through platforms that do not yet support the standard. If a copy of that metadata is retained in a database, a watermark actively inserted into the content, or a fingerprint passively computed from the content, can be used to look up that information from the database. These two technologies may be combined for further robustness. This is referred to as a Durable Content Credential.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I incorporated your suggestions and reworded the text substantially.

One further question: You say "Durable CC" .. combines "either, or both of, invisible watermarking, and content fingerprinting" but the blog post indicates that it's ALL THREE (not just 2 of 3):

...watermarking ... is only one piece in the three-pronged approach represented by Content Credentials.

and

... But combined into a single approach, the three form a unified solution that is robust and secure ....

I'll try to get some clarity from @andyparsons .

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the currently committed wording is ok. I agree with your point of accuracy that ‘Durable Content Credential’ as we originally introduced it does mean all three. The use of the term in the community has broadened a bit to general manifest recovery. I think the current text captures both in the sense that it names the technology pillars and states that combining them adds robustness, so in that sense it is clear enough.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That said there is a typo now ' persist across content platforms by also:' doesn't scan.. should be maybe ' persist across content platforms through use of: '


In the C2PA specification, watermarks and content fingerprinting are known as [soft bindings](https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_soft_bindings), which enable digital content to be matched even if the underlying bits differ. For example, if manifest metadata has been stripped from an asset, then you can use a soft binding to look up and recover the manifest from an online repository.

The C2PA specification requires that soft bindings be generated using one of the [soft binding algorithms](https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_soft_binding_algorithm_list) approved by the C2PA Technical Working Group. The following table lists the algorithms from the [C2PA Soft Binding Algorithm List](https://github.com/c2pa-org/softbinding-algorithm-list).

import JSONToTable from '@site/src/components/JSONToTable';

<JSONToTable />
5 changes: 5 additions & 0 deletions scripts/fetch-readme.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ const readmes = [
repo: 'contentauth/c2pa-min',
path: 'README.md',
},
{
dest: resolve(__dirname, '../static/sb-alg-list.json'),
repo: 'c2pa-org/softbinding-algorithm-list',
path: 'softbinding-algorithm-list.json',
},
];

function resolveMarkdownLinks(linkBase, content) {
Expand Down
13 changes: 11 additions & 2 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,17 @@ const sidebars = {
id: 'faqs',
},
{
type: 'doc',
id: 'community-resources',
type: 'category',
label: 'Community resources',
link: { type: 'doc', id: 'community-resources' },
collapsed: true,
items: [
{
type: 'doc',
label: 'Watermarking and fingerprinting',
id: 'sb-algs',
},
],
},
{
type: 'doc',
Expand Down
130 changes: 130 additions & 0 deletions src/components/JSONToTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { useState, useEffect } from 'react';

// Define the fields to display and their custom headers
const DISPLAY_FIELDS = {
alg: 'Algorithm Name',
decodedMediaTypes: 'Media Types',
'entryMetadata.informationalUrl': 'URL',
'entryMetadata.contact': 'Contact',
};

// Helper function to safely extract nested values and flatten arrays
const getNestedValue = (obj, path) => {
const value = path
.split('.')
.reduce(
(acc, key) => (acc && acc[key] !== undefined ? acc[key] : 'N/A'),
obj,
);
return Array.isArray(value) ? value.join(', ') : value; // Flatten arrays into a comma-separated string
};

// Function to extract and filter only the required fields
const extractAndFilterObject = (obj) => {
let result = {};
for (const key in DISPLAY_FIELDS) {
result[key] = getNestedValue(obj, key);
}
return result;
};

// Helper function to capitalize each word in a string
const capitalizeWords = (str) => {
return str
.split(' ')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
};

const JSONToTable = () => {
const [jsonData, setJsonData] = useState(null);
const [error, setError] = useState(null);

useEffect(() => {
fetch('/sb-alg-list.json')
.then((response) => {
if (!response.ok) {
throw new Error('Failed to fetch the JSON file');
}
return response.json();
})
.then((data) => {
setJsonData(data);
setError(null);
})
.catch((err) => {
setError(err.message);
setJsonData(null);
});
}, []);

// Function to group data by the 'type' field
const groupDataByType = (data) => {
return data.reduce((acc, item) => {
const type = getNestedValue(item, 'type');
if (!acc[type]) acc[type] = [];
acc[type].push(item);
return acc;
}, {});
};

const generateTableHeaders = () => {
return Object.keys(DISPLAY_FIELDS).map((key) => (
<th key={key}>{DISPLAY_FIELDS[key]}</th>
));
};

const generateTableRows = (data) => {
return data.map((item, rowIndex) => {
const filteredData = extractAndFilterObject(item);
const url = filteredData['entryMetadata.informationalUrl'];
const description = getNestedValue(item, 'entryMetadata.description');

return (
<tr key={rowIndex}>
{Object.keys(DISPLAY_FIELDS).map((key, index) => (
<td key={index}>
{key === 'entryMetadata.informationalUrl' ? (
url !== 'N/A' && description !== 'N/A' ? (
<a href={url} target="_blank" rel="noopener noreferrer">
{description}
</a>
) : (
'N/A'
)
) : (
filteredData[key]
)}
</td>
))}
</tr>
);
});
};

if (error) return <p style={{ color: 'red' }}>{error}</p>;

if (!jsonData || !Array.isArray(jsonData)) {
return <p>No data found in the JSON. Expected an array of objects.</p>;
}

const groupedData = groupDataByType(jsonData);

return (
<div>
{Object.entries(groupedData).map(([type, data]) => (
<div key={type} style={{ marginBottom: '30px' }}>
<h2>{capitalizeWords(type)} algorithms</h2>
<table style={{ borderCollapse: 'collapse' }}>
<thead>
<tr>{generateTableHeaders()}</tr>
</thead>
<tbody>{generateTableRows(data)}</tbody>
</table>
</div>
))}
</div>
);
};

export default JSONToTable;
158 changes: 158 additions & 0 deletions static/sb-alg-list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
[
{
"identifier": 1,
"alg": "com.digimarc.validate.1",
"type": "watermark",
"decodedMediaTypes": ["audio", "video", "text", "image"],
"entryMetadata": {
"description": "Digimarc Validate Digital Watermarking algorithm",
"dateEntered": "2024-05-17T17:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://www.digimarc.com/products/digital-content-authentication"
}
},
{
"identifier": 2,
"alg": "org.atsc.a336",
"type": "watermark",
"decodedMediaTypes": ["audio", "video", "image"],
"entryMetadata": {
"description": "ATSC watermarking (A/334, A/335, A/336)",
"dateEntered": "2024-05-17T15:43:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://www.atsc.org/atsc-documents/a3362017-content-recovery-redistribution-scenarios/"
}
},
{
"identifier": 3,
"alg": "io.iscc.v0",
"type": "fingerprint",
"decodedMediaTypes": ["text", "image", "audio", "video", "application"],
"entryMetadata": {
"description": "ISO 24138 - International Standard Content Code (ISCC) V0 algorithm",
"dateEntered": "2024-05-17T16:00:00Z",
"contact": "[email protected]",
"informationalUrl": "https://www.iso.org/standard/77899.html"
}
},
{
"identifier": 4,
"alg": "com.adobe.trustmark.Q",
"type": "watermark",
"decodedMediaTypes": ["image"],
"entryMetadata": {
"description": "Adobe Trustmark variant Q",
"dateEntered": "2024-05-17T17:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://github.com/adobe/trustmark/"
}
},
{
"identifier": 5,
"alg": "com.adobe.trustmark.C",
"type": "watermark",
"decodedMediaTypes": ["image"],
"entryMetadata": {
"description": "Adobe Trustmark variant C",
"dateEntered": "2024-05-17T17:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://github.com/adobe/trustmark/"
}
},
{
"identifier": 6,
"alg": "com.adobe.icn.dense",
"type": "fingerprint",
"decodedMediaTypes": ["image"],
"entryMetadata": {
"description": "Adobe Image Comparator Network Dense Fingerprint",
"dateEntered": "2024-05-17T17:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://openaccess.thecvf.com/content/CVPR2021W/WMF/html/Black_Deep_Image_Comparator_Learning_To_Visualize_Editorial_Change_CVPRW_2021_paper.html"
}
},
{
"identifier": 7,
"alg": "ai.steg.api",
"type": "watermark",
"decodedMediaTypes": ["image", "video", "application"],
"entryMetadata": {
"description": "Steg.AI invisible watermarking",
"dateEntered": "2024-05-20T10:50:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://openaccess.thecvf.com/content_CVPR_2019/papers/Wengrowski_Light_Field_Messaging_With_Deep_Photographic_Steganography_CVPR_2019_paper.pdf"
}
},
{
"identifier": 8,
"alg": "ai.trufo.gen1.image",
"type": "watermark",
"decodedMediaTypes": ["image"],
"entryMetadata": {
"description": "Trufo image watermark.",
"dateEntered": "2024-08-14T15:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://trufo.ai/publications/"
}
},
{
"identifier": 9,
"alg": "ai.trufo.gen1.image-lite",
"type": "watermark",
"decodedMediaTypes": ["image"],
"entryMetadata": {
"description": "Trufo image watermark (lite version).",
"dateEntered": "2024-08-14T15:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://trufo.ai/publications/"
}
},
{
"identifier": 10,
"alg": "app.overlai.watermark.1",
"type": "watermark",
"decodedMediaTypes": ["image"],
"entryMetadata": {
"description": "Overlai Watermark version 1",
"dateEntered": "2024-08-14T16:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://overlai.app/watermark"
}
},
{
"identifier": 11,
"alg": "tv.kinetiq.watercast.48.1",
"type": "watermark",
"decodedMediaTypes": ["video"],
"entryMetadata": {
"description": "Teletrax Watermarking algorithm",
"dateEntered": "2024-10-16T17:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://kinetiq.tv/broadcasting-syndication/"
}
},
{
"identifier": 12,
"alg": "castLabs.watermark.1",
"type": "watermark",
"decodedMediaTypes": ["video"],
"entryMetadata": {
"description": "castLabs Single Frame Forensic Watermarking",
"dateEntered": "2024-11-13T12:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://castlabs.com/image-watermarking/"
}
},
{
"identifier": 13,
"alg": "com.adobe.trustmark.P",
"type": "watermark",
"decodedMediaTypes": ["image"],
"entryMetadata": {
"description": "Adobe Trustmark variant P",
"dateEntered": "2025-02-05T17:00:00.000Z",
"contact": "[email protected]",
"informationalUrl": "https://github.com/adobe/trustmark/"
}
}
]
Loading