export type AIInferenceResult = { aiGenerated: boolean; aiProbability: number; humanProbability: number; }; export const C2PASourceTypeScreenCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/screenCapture"; export const C2PASourceTypeDigitalCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture"; export const C2PASourceTypeComputationalCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture"; export const C2PASourceTypeCompositeCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/compositeCapture"; export const C2PASourceTypeTrainedAlgorithmicMedia = "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"; export const C2PASourceTypeCompositeWithTrainedAlgorithmicMedia = "http://cv.iptc.org/newscodes/digitalsourcetype/compositeWithTrainedAlgorithmicMedia"; export type C2PAActionsAssertion = { actions: { action: string; softwareAgent?: string; digitalSourceType?: string; }[]; }; export type C2PAAssertion = { label: string; data: C2PAActionsAssertion | undefined; }; export type C2PAManifest = { assertions: C2PAAssertion[]; signature_info: { time: string; }; }; export type C2PAValidationResults = { activeManifest?: { failure: any[]; success: any[]; informational: any[]; }; }; export type C2PAManifestInfo = { active_manifest: string; manifests: { [key: string]: C2PAManifest }; validation_results?: C2PAValidationResults; }; export type C2PAData = { manifest_info: C2PAManifestInfo; }; export type Proof = { data?: any; name?: string; json?: string; integrity?: { pgp?: any; c2pa?: C2PAData; exif?: { [key: string]: string | Object }; opentimestamps?: any }; ai?: { inferenceResult?: AIInferenceResult }; }; export type ProofHintFlags = { aiGenerated?: boolean; aiEdited?: boolean; screenshot?: boolean; camera?: boolean; }; type FlagMatchRule = { field: string; match: string[]; description: string; }; type FlagMatchInfo = { field: string; value: string; re: string; }; type FlagMatch = { re: RegExp; value: string; }; const ruleScreenshot = (): FlagMatchRule[] => { return [ { field: "integrity/c2pa/manifest_info/manifests[]/assertions[]/{label=c2pa.actions}/data/actions[]/{action=c2pa.created}/digitalSourceType", match: [C2PASourceTypeScreenCapture], description: "Screen capture", }, ]; }; const aiHintFlags = (): FlagMatchRule[] => { const knownAIServices = [ "ChatGPT", "OpenAI-API", "Adobe Firefly", "RunwayML", "Runway AI", "Google AI", "Stable Diffusion", ]; return [ { field: "name", match: ["^DALL_E_", "^Gen-3"], description: "File name" }, { field: "integrity/c2pa/manifest_info/manifests[]/claim_generator", match: knownAIServices, description: "C2PA claim generator", }, { field: "iptc/Credit", match: knownAIServices, description: "IPTC Credit" }, { field: "iptc/Provider", match: knownAIServices, description: "IPTC Provider" }, { field: "iptc/ImageSupplier[]", match: knownAIServices, description: "IPTC ImageSupplier" }, { field: "iptc/ImageCreator[]", match: knownAIServices, description: "IPTC ImageCreator" }, ]; }; const matchFlag = (rules: FlagMatchRule[], file: any) => { const matchParts = (parts: string[], o: any, re: RegExp[]): FlagMatch[] | undefined => { if (parts.length == 0 || o == undefined) return undefined; let part = parts[0]; if (part.endsWith("[]")) { part = part.substring(0, part.length - 2); if (o[part] != undefined) { let opart: any[] = o[part]; if (!Array.isArray(opart)) { opart = Object.values(opart) ?? []; } if (parts.length == 1) { let strings = opart as string[]; if (!strings) return undefined; return strings.reduce((res: FlagMatch[] | undefined, s: any) => { return re.reduce((res: FlagMatch[] | undefined, r: any) => { if (r.test(s)) { const r2 = res || []; r2.push({ re: r, value: s as string }); return r2; } return res; }, res); }, undefined); } else { return opart.reduce((res: FlagMatch[] | undefined, o: any) => { let matches = matchParts(parts.slice(1), o, re); if (matches) { const r2 = res || []; r2.push(...matches); return r2; } return res; }, undefined); } } else { return undefined; } } else if (part.startsWith("{") && part.endsWith("}")) { const [prop, val] = part.substring(1, part.length - 1).split("="); if (o[prop] === val) { return matchParts(parts.slice(1), o, re); } else { return undefined; } } else { if (o[part] != undefined) { if (parts.length == 1) { return re.reduce((res: FlagMatch[] | undefined, r: any) => { if (r.test(o[part])) { const r2 = res || []; r2.push({ re: r, value: o[part] as string }); return r2; } return res; }, undefined); } else { return matchParts(parts.slice(1), o[part], re); } } else { return undefined; } } }; let result = false; let resultInfo: FlagMatchInfo[] = []; for (let rule of rules) { try { const re: RegExp[] = (!Array.isArray(rule.match) ? [rule.match] : rule.match).map((m) => new RegExp(m, "gi")); let parts = rule.field.split("/"); let matches = matchParts(parts, file, re); if (matches) { matches.forEach((m) => { resultInfo.push({ field: rule.description, value: m.value, re: m.re.source }); }); result = true; } } catch (e) { console.error("Invalid RE", e); } } return { result: result, matches: resultInfo }; }; export const extractProofHintFlags = (proof?: Proof): ProofHintFlags | undefined => { if (!proof) return undefined; let foundCreated = false; let screenshot = false; let camera = false; let aiGenerated = false; let aiEdited = false; let valid = false; try { let results = proof.integrity?.c2pa?.manifest_info.validation_results?.activeManifest; if (results) { valid = results.failure.length == 0 && results.success.length > 0; } const manifests = Object.values(proof.integrity?.c2pa?.manifest_info.manifests ?? {}); for (const manifest of manifests) { for (const assertion of manifest.assertions) { if (assertion.label === "c2pa.actions") { const actions = (assertion.data as C2PAActionsAssertion)?.actions ?? []; const a = actions.find((a) => a.action === "c2pa.created"); if (a) { // creator.value = a.softwareAgent; // dateCreated.value = dayjs(Date.parse(manifest.signature_info.time)); if (a.digitalSourceType === C2PASourceTypeScreenCapture) { screenshot = true; } if ( a.digitalSourceType === C2PASourceTypeDigitalCapture || a.digitalSourceType === C2PASourceTypeComputationalCapture || a.digitalSourceType === C2PASourceTypeCompositeCapture ) { camera = true; } if ( a.digitalSourceType === C2PASourceTypeTrainedAlgorithmicMedia || a.digitalSourceType === C2PASourceTypeCompositeWithTrainedAlgorithmicMedia ) { aiGenerated = true; } foundCreated = true; break; } } if (foundCreated) { break; } } if (foundCreated) { break; } } if (valid) { screenshot = matchFlag(ruleScreenshot(), proof).result; const flags: ProofHintFlags = { aiGenerated, aiEdited, screenshot, camera, }; return flags; } } catch (error) {} return undefined; };