Architecture3 min

Build vs. Buy für Provenance: Warum wir die C2PA-Engine selbst besitzen

Build vs. Buy für Provenance: Warum wir die C2PA-Engine selbst besitzen

Die naheliegende Wahl war, keine weitere C2PA-Implementierung zu bauen. Ausgereifte Werkzeuge existieren, gepflegt von Entwicklern, die den Standard in- und auswendig kennen. Wir haben uns dennoch entschieden, eine eigene Engine von Grund auf zu entwickeln. Dieser Beitrag erklärt die architektonischen Beweggründe, was uns dieser Schritt ermöglicht hat und was er im laufenden Betrieb tatsächlich kostet. Wer Build-vs-Buy für Vertrauensinfrastruktur evaluiert, findet hier das Entscheidungsprotokoll, das wir gern vor Beginn des Projekts gelesen hätten.

Die Entscheidung an Anforderungen festmachen, nicht an Technologievorlieben

Architekturentscheidungen, die mit Vorlieben für bestimmte Programmiersprachen beginnen, führen meist in die Irre. Ausgangspunkt muss immer das fachliche Anforderungsprofil des Produkts sein. Fünf harte Vorgaben kollidierten kontinuierlich mit den Grenzen bestehender Standard-Laufzeiten:

1. Die Engine muss im Browser und auf mobilen Endgeräten laufen. Beweise werden zum Zeitpunkt der Erfassung unmittelbar auf dem Endgerät versiegelt. Die gesamte Signatur-Pipeline, die JUMBF-Serialisierung und das Hashen der Mediendateien müssen direkt dort ausgeführt werden, wo die Daten entstehen. Hier klafft im offiziellen Tooling eine entscheidende Lücke: c2pa-rs ist in Rust verfasst, und das offizielle c2pa-js (@contentauth/c2pa-web) ist im Client lediglich ein lesender Prüfer, der keine Manifeste erstellen oder signieren kann. Selbst wenn man die Signatur nach WebAssembly kompilieren würde: WASM leidet unter Kaltstarts, hat keinen direkten Zugriff auf native Sicherheitshardware und scheidet in eingebetteten mobilen Laufzeiten wie Apples JavaScriptCore unter iOS völlig aus, da dort keine WebAssembly-Ausführung unterstützt wird.

2. Große Videodateien müssen gestreamt werden. Versicherungsvideos und Drohnenaufnahmen passen nicht in den flüchtigen Arbeitsspeicher eines mobilen Tabs. Die Engine muss Dateien im Gigabyte-Bereich hashen, signieren und im Container umschreiben können, ohne sie als Ganzes im RAM zu materialisieren. Das verlangt die volle Kontrolle über Segmentlisten und Chunk-Größen beim Dateizugriff. Eine gewrappte Fremdlaufzeit stellt lediglich eine API-Schnittstelle bereit, bietet jedoch kein anpassbares Speichermodell.

3. Eigene Assertion-Typen müssen nativ unterstützt werden. Hochpräzise GPS-Spuren, Sensordaten, Kennzeichenerkennungen und Kamera-Telemetrie sind kein Bestandteil des regulären C2PA-Standardvokabulars. Unsere TrustLabelAssertion bettet diese domänenspezifischen Daten direkt in das signierte Manifest ein. Um das Assertion-Framework sauber zu erweitern, muss die Registrierung, Serialisierung und verlustfreie Wiederherstellung vollständig kontrolliert werden.

4. Provenance-Ketten müssen beim erneuten Signieren lückenlos erhalten bleiben. Beweise durchlaufen typischerweise mehrere Verarbeitungsstufen. Jede Stufe muss ein neues Manifest erzeugen, das das vorherige als übergeordnetes Ingrediens (ingredient) referenziert, anstatt es einfach zu überschreiben. Die Unterscheidung zwischen c2pa.created und c2pa.opened sowie die korrekte Verknüpfung von activeManifest und validationResults (beim Übergang von V2 auf V3) sind unternehmenskritische Produktentscheidungen und keine bloßen Standard-Voreinstellungen einer Drittbibliothek.

5. Die Integration mit Zertifikatsausstellung und Zeitstempeldiensten muss über minimale Schnittstellen erfolgen. Unser Signer-Interface umfasst exakt drei Methoden. Das TimestampProvider-Interface besteht aus einer einzigen Methode. Nichts darüber hinaus passiert diese Systemgrenze. Eine eingebundene Komplettlösung bringt meist feste Annahmen über Zertifizierungsstellen (CAs) und Zeitstempelbehörden (TSAs) mit – wodurch eine strategische Produktentscheidung plötzlich von einer externen Abhängigkeit diktiert wird.

Jede Anforderung für sich genommen ließe sich vielleicht kompromissbehaftet lösen. Zusammengenommen beschreiben sie eine eigenständige Engine und keine bloße Bibliotheksintegration.

Was Eigentümerschaft verlangt

Die Engine selbst zu besitzen bedeutet, die Verantwortung für alle Schichten unterhalb der Abstraktionsgrenze zu übernehmen.

Asset-Container direkt verarbeiten. Das Parsen von JPEG-APP11-Segmenten (Erkennung von 0xEB-Markern, JUMBF-Header-Parsing, Zusammenfügen segmentierter Blöcke), chirurgische Eingriffe in BMFF-Boxen (Strukturierung von ftyp, moov und mdat, Patchen von stco-, co64- und iloc-Offsets), Chunk-Verarbeitung in PNGs inklusive Neuberechnung von Prüfsummen (CRCs) sowie Frame-Scanning in MP3-Dateien. Jedes Format besitzt eigene Parser-Logiken, spezifische Hash-Ausschlussregeln und Eigenheiten: JPEGs beschränken Segmente auf 64 KB; BMFF erfordert das Verschieben absoluter Offsets, sobald das Manifest die Mediendaten versetzt.

COSE-Signierung und RFC-3161-Zeitstempel. Die Signatur-Pipeline erzeugt COSE-Sign1-Strukturen, validiert X.509-Zertifikatsketten und holt qualifizierte Zeitstempel gemäß RFC 3161 von unserer TSA ein. Die Schnittstelle für Zeitstempel ist denkbar schlank: Bytes übergeben, Gegensignatur erhalten. Alles dahinter lässt sich modular austauschen.

Differenzierte Validierung. Die Validierung liefert strukturierte, an der C2PA-Spezifikation ausgerichtete Statuscodes (wie ClaimSignature.validated, AssertionHashedURI.mismatch oder ClaimSignature.trustedTime.mismatch) anstelle eines simplen Booleans zurück. Unsere Benutzeroberfläche und die Ingredient-Assertions bauen unmittelbar auf diesen Detailcodes auf. Ein schlichtes True/False würde genau jene forensischen Informationen vernichten, die für Audit- und Prüfansichten unerlässlich sind.

Dieser Ansatz ist anspruchsvoll: Jede Unklarheit in den Entwürfen der C2PA-Spezifikation wird unmittelbar zur eigenen Entwicklungsaufgabe – und davon gibt es in der Praxis mehr als vermutet.

Was Eigentümerschaft ermöglicht

Die strategischen Vorteile zeigen sich an Stellen, die eine eingebundene Drittbibliothek prinzipbedingt nicht abdecken kann.

Eine einheitliche Codebasis für vier Laufzeiten mit dedizierten mobilen Build-Targets. Dieselbe TypeScript-Bibliothek läuft ohne Modifikation im Browser (via WebCrypto und @noble/hashes) und in serverlosen Umgebungen wie AWS Lambda (über Node.js-Crypto). Entscheidend ist jedoch: esbuild kompiliert den Code direkt für spezifische mobile Zielplattformen – mit target: 'ios16' in ein eingebettetes Bundle für sdk-ios (das nativ in Apples JavaScriptCore ohne WASM läuft) und als es2022-IIFE für sdk-android. Die JavaScript-Engine steuert die Manifest-Assemblierung und das Hashing, während die kryptografische Signatur direkt an die Hardware-Sicherheitsanker des Geräts (Apple Secure Enclave via Keychain, Android Keystore) delegiert wird. Ein nach WASM kompiliertes Rust-Modul würde pro Plattform separate FFI-Bindeglieder und Architekturbuilds erfordern und ließe sich in JavaScriptCore gar nicht erst starten.

Die TrustLabelAssertion. Unsere spezialisierte Assertion für Erfassungs- und Sensormetadaten existiert nur, weil wir das Assertion-Framework selbst steuern. CustomJsonAssertion<T> ist eine schlanke Basisklasse von rund 40 Zeilen, die JUMBF-Serialisierung, Typsicherheit, Fehlerprüfungen und Namespaces kapselt. Ein neuer Assertion-Typ erfordert lediglich eine minimale Unterklasse. Bei einer Fremdbibliothek wäre hierfür jedes Mal ein Upstream-Feature-Request oder eine komplexe Interop-Schicht notwendig.

Lückenloser Erhalt von Provenance-Ketten. Erkennt unsere Protect-Pipeline ein bereits bestehendes Manifest in einer Datei, liest sie dieses ein, erzeugt daraus ein ingredient, das das aktive Manifest referenziert, und bettet dieses in ein neues Manifest mit der Aktion c2pa.opened ein. Das vorherige Manifest bleibt als verifizierbarer Vorgänger erhalten und wird nicht blind gelöscht. Ob c2pa.created oder c2pa.opened verwendet wird, wie validationResults eingebunden werden und wie Versionsunterschiede zwischen V2 und V3 gehandhabt werden, sind bewusste Produktentscheidungen, die im eigenen Code sauber abgebildet sind.

Performante Streaming-Pipeline für Videos. Der BlobDataReader mit lazy Slices, 64-MB-Chunks und Segment-Spleißung funktioniert deshalb so zuverlässig, weil wir den Byte-Fluss durch das Gesamtsystem selbst kontrollieren. Dasselbe gilt für hashWithExclusions mit 1-MB-Blöcken und Offset-Marker-Behandlung. Ein Wrapper müsste diese Interna entweder vollständig offenlegen oder würde dem Gesamtsystem ein fremdes, oft ineffizientes Speichermodell aufzwingen.

Ein ehrlicher Blick auf die laufenden Kosten

Wer eine eigene Engine betreibt, zahlt regelmäßig drei konkrete Rechnungen:

Umgang mit Spec-Mehrdeutigkeiten. Das entries-Feld in Trainings- und Mining-Assertionen liegt je nach erzeugender Implementierung auf unterschiedlichen Hierarchieebenen. Das Feld digitalSourceType nutzt uneinheitlich http:// oder https://. Aktions-Assertionen unterscheiden sich zwischen V1 und V2 in Feldbezeichnungen (softwareAgent gegenüber softwareAgentIndex). Für jede dieser Inkonsistenzen pflegen wir tolerante Reader und exakte Writer. Die Anzahl dieser Ausnahmeregeln wächst mit der Zeit und schrumpft selten.

Kontinuierliches Standards-Tracking. Die C2PA-Spezifikation umfasst inzwischen drei Claim-Versionen (V1, V2, V3). Parallel dazu definiert die CAWG (Creator Assertions Working Group) neue Community-Assertionen, während sich IPTC-Vokabulare unabhängig weiterentwickeln. Schritt zu halten bedeutet, Entwürfe aufmerksam zu analysieren, Breaking Changes frühzeitig zu erkennen und die Bibliothek zu aktualisieren, bevor Endnutzer auf inkompatible Fremddateien stoßen. Wir verlinken Upstream-Issues konsequent in Code-Kommentaren, um den Kontext auch bei personellen Wechseln im Team zu sichern.

Aufwendige Interoperabilitäts-Tests. Eigene Dateien müssen in Fremdwerkzeugen fehlerfrei validieren. Wir testen fortlaufend gegen den offiziellen C2PA-Referenzprüfer, die Content-Credentials-Tools von Adobe und das CLI-Tool der C2PA. Dateien, die in unserer Bibliothek als gültig markiert werden, in externen Tools aber scheitern, betrachten wir als eigene Fehler. Umgekehrt verlangen Dateien, die extern validieren, bei uns aber fehlschlagen, meist nach einem weiteren toleranten Reader-Pfad.

Benötigt ein Produkt die oben genannten fünf Kernanforderungen nicht, sollte man diese Kosten keinesfalls in Kauf nehmen. In diesem Fall ist das Einbinden der bestehenden Referenzwerkzeuge der wirtschaftlich sinnvollere Weg.

Die übergeordnete Erkenntnis

Echte Produktdifferenzierung in der Infrastruktur entsteht häufig dadurch, dass man exakt diejenige Abstraktionsschicht selbst besitzt, die das eigene Produkt maßgeblich erweitern muss. Der eigentliche Wert liegt hier nicht in der Wahl von TypeScript anstelle von Rust. Er liegt in der uneingeschränkten Kontrolle über die Vertrauensgrenze: exakt an dem Punkt, an dem Rohdaten zu fälschungssicheren Beweisen werden.

Diese Richtungsentscheidung bleibt fortlaufend überprüfbar. Eine eigene Engine rechtfertigt ihren Aufwand nur so lange, wie die Produktanforderungen über das hinausgehen, was Standard-Frameworks leisten können. Sollten die Referenzimplementierungen eines Tages Browser-Ausführung, Bounded-Memory-Streaming, benutzerdefinierte Assertionen und saubere Provenance-Ketten vollständig und elegant abdecken, wird die Situation neu bewertet. Dieser Zeitpunkt ist heute jedoch noch nicht erreicht.

Häufige Fragen

Warum nicht c2pa-js oder c2pa-rs einbinden? Einbinden bedeutet, die Einschränkungen eines Fremdsystems zu erben. Das offizielle c2pa-js ist im Client ein reines Prüfwerkzeug ohne jede Möglichkeit zur Signaturerstellung. Und c2pa-rs kompiliert zu WebAssembly, was in Apples nativem JavaScriptCore auf iOS nicht lauffähig ist und keinen Zugriff auf die Secure Enclave bietet. Wir brauchten Client-Signierung am Erfassungsort, Streaming riesiger Assets und eigene Assertionen – Anforderungen, die wir detailliert im Beitrag C2PA-Bibliothek von Grund auf analysieren.

Bedeutet eine eigene Engine, dass man das C2PA-Ökosystem ignoriert? Das Gegenteil ist der Fall. Da Interoperabilität eine unverzichtbare Kernanforderung ist, verfolgen wir die Spezifikation sehr eng, dokumentieren Arbeitsgruppen-Diskussionen im Code und halten Reader tolerant und Writer normtreu. Unsere Testsuite enthält fortlaufend Testdateien anderer Implementierer.

Wie wird die Kompatibilität bei Versionssprüngen der Spezifikation gewahrt? Durch parallele Codepfade für V1-, V2- und V3-Claims sowie eine strikte Konformitätsphilosophie: Gültig ist das, was spezifikationskonform über die Leitung übertragen wird, nicht das Verhalten einer einzelnen Referenzbibliothek.

Würden Sie diese Entscheidung heute wieder so treffen? Bei identischen Anforderungen an Browserbetrieb, Streaming und Sensorversiegelung: uneingeschränkt ja. Für ein enger gestecktes Produkt, etwa eine reine serverseitige Signaturprüfung, würden wir ohne Zögern auf bestehende Rust-Module zurückgreifen.

Beteiligen Sie sich an der Weiterentwicklung der Standards? Wir melden Unklarheiten in der Spezifikation und Konformitätsabweichungen regelmäßig an die zuständigen Arbeitsgruppen zurück. Während unsere Backend-Pipelines proprietäre Hardware-Semantiken nutzen, ist unsere Kern-Engine frei verfügbar als c2pa-ts auf GitHub. Interoperabilitäts-Erkenntnisse fließen kontinuierlich in den Standardisierungsprozess ein. Vertrauensinfrastruktur floriert durch ein starkes Ökosystem weitaus mehr als durch isolierten Lock-in.

Neueste Artikel

Ihr Weg zu uns!

Möchten Sie mehr erfahren?