FHE verändert die Spielregeln und wir sind alle eingeladen, mitzumachen.
Wenn Sie keine Ahnung haben, wovon ich spreche, bedeutet das, dass Sie in letzter Zeit unter einem Stein gelebt haben. Durchsuchen Sie einige der umfassenden Ressourcen von FHE.org und kommen Sie zurück.
Damit FHE sein Versprechen gegenüber der Technologiewelt erfüllen kann, muss es mit einer neuen Generation industrietauglicher Tools für Entwicklung, Kompilierung und Laufzeitausführung einhergehen, mit denen jeder problemlos homomorphe Apps erstellen kann.
Derzeit investieren viele Experten und Unternehmen im FHE-Bereich jedoch immer noch den Großteil ihrer Zeit und Anstrengungen in die Verbesserung der Kryptografie hinter FHE, anstatt sich auf die Entwicklung großartiger Entwicklungstools für Laien zu konzentrieren. Hören Sie mir zu: Die Verbesserung der Kernfunktionen und der Leistung von FHE ist immer eine gute Nachricht. Aber im Großen und Ganzen fördern diese schrittweisen Verbesserungen die globale Akzeptanz bestenfalls geringfügig. Sie werden sich irgendwann auf die Akzeptanz auswirken, aber nicht jetzt .
Aus meiner Sicht ist es offensichtlich, dass die Tech-Welt heute leistungsstarke, entwicklerfreundliche FHE-Toolchains braucht, um FHE-gestützte Erfolgsgeschichten zu schreiben und FHE von einem Tech-Trend zu einem echten Paradigmenwechsel im digitalen Sicherheitsgeschäft zu machen. Ich glaube, dass der aktuelle Wissensstand über FHE – sowohl wissenschaftlich als auch technologisch – bereits ausreicht, um solche Tools jetzt zu entwickeln und sie ohne weitere Verzögerung der technisch versierten Masse zur Verfügung zu stellen. Die kontinuierliche Integration neuer Funktionen wird sich im Laufe der Zeit natürlich ergeben, das ist immer der Fall.
Aber hier ist der Punkt: FHE gibt es in vielen Varianten. Je nachdem, welches kryptografische Schema Sie verwenden – oder welchen konkreten Einsatz Sie davon machen – ergibt sich eine andere Art, Berechnungen darzustellen und homomorphe Programme auszuführen. Es ist, als wären FHE-Schemata völlig unterschiedliche Tiere, eines liefert Ihnen Turingmaschinen und ein anderes Lambda-Kalkül. Biodiversität ist in der Technik oder anderswo immer gut, aber es bedeutet auch, dass Sie eine praktikable Strategie festlegen müssen, wenn Sie FHE in der Praxis einsetzen.
Meine Firma Zama konzentriert sich auf ein bestimmtes FHE-Schema, nämlich TFHE . TFHE erreicht homomorphe Datenverarbeitung mit ganz bestimmten Eigenschaften: superschnelles Bootstrapping und Berechnungen, ausgedrückt als Netzwerke von Tabellensuchen. Wir haben ein tiefes Verständnis dafür entwickelt, wie diese Besonderheiten – die TFHE früher zu einer Art Außenseiter im FHE-Bereich machten – in homomorphe Bibliotheken, Kompilierung, virtuelle Maschinen oder Hardwarebeschleunigung umgesetzt werden können.
Andere prominente FHE-Konkurrenten wie CKKS , BGV oder BFV beinhalten sehr unterschiedliche Konzepte in ihrer praktischen Instrumentierung: Bootstrappings sind zu langsam, um eine Option zu sein, daher ist die Verarbeitung in ihrer Tiefe begrenzt, Daten können jedoch mit massivem Batching vektorisiert werden, Berechnungen werden als Polynomschaltungen ausgedrückt und - im Fall von CKKS - sind die Ergebnisse ungefähr. Die Umsetzung von BGV/BFV und CKKS in Compiler und Laufzeiten erfordert daher eine völlig andere Denkweise von den Tool-Entwicklern.
Entwicklern dürfte es allerdings kaum wichtig sein, welches spezielle Schema ihre FHE-Toolchain und Laufzeitumgebungen antreibt, solange sie einfach zu bedienen sind und die Leistungsanforderungen in eingesetzten homomorphen Anwendungen erfüllt werden. Sie sind selbst kreative Entwickler und ihr Fokus sollte auf den neuen Erfahrungen liegen, die sie ihren Benutzern bieten können.
Das Endziel für die FHE-Technologie-Enabler besteht also darin, Tools zu konzipieren und bereitzustellen, die nicht nur für ihre Hauptsendezeit im Dev-Multiversum bereit sind, sondern auch stark genug, um einen neuen Industriestandard zu setzen. Um das zu erreichen, müssen sie das Risiko eingehen und entscheiden, welches FHE-Schema sie eher dorthin bringt.
Lassen Sie uns ein Spiel spielen.
Nehmen Sie einen Entwickler, der nichts von den Feinheiten von FHE weiß, aber eine homomorphe Anwendung erstellen möchte. Sie sind hier der Tool-Entwickler, Sie stehen diesem Entwickler gegenüber, von dem Sie eine gewisse Vertrautheit mit gängigen Entwicklungspraktiken und den Grundlagen der Informatik erwarten können, aber alles andere – höhere Mathematik und dergleichen – ist tabu. Wie können Sie ihn erfolgreich dazu bringen, die FHE-App selbst zu erstellen?
Dieser Artikel untersucht verschiedene Strategien, um dieses Spiel durch Wetten auf TFHE zu gewinnen.
Je nach Art der Anwendung – benutzerdefinierte Datenverarbeitung, neuronale Netzwerke, Smart Contracts oder allgemeine Programmierung – werden TFHE-gestützte Erfolgspfade erkundet. Diese Erkundung führt uns auf einen einfachen Weg, einen schwierigen Weg und einige andere dazwischen, mit unterschiedlichen Graden der technologischen Reife in ihrer praktischen Umsetzung.
TFHE steht für Torus FHE. TFHE wird nach den Namen seiner Entdecker auch CGGI genannt und nimmt innerhalb der FHE-Landschaft eine einzigartige Position ein: Es ist der bekannteste Mechanismus, der programmierbares Bootstrapping (PBS) ermöglicht.
Kurz gesagt ist ein PBS eine homomorphe Tabellensuche. Es gibt eine Verschlüsselung von T[x]
zurück, wobei T
eine tabellierte Funktion Ihrer Wahl ist, gegeben eine Verschlüsselung eines Index x
. Seine Laufgeschwindigkeit hängt nicht von den Einträgen von T
ab, sondern nur von der Anzahl der Einträge und liegt im Bereich von Millisekunden. Außerdem setzt ein PBS das in seinem Ausgabe-Chiffretext eingebettete Verschlüsselungsrauschen zurück, sodass Sie PBSs unbegrenzt nacheinander zusammenstellen können, da Sie wissen, dass Ihre homomorphe Anwendung immer saubere Chiffretexte verarbeiten wird.
Das von TFHE befürwortete Computermodell ist von grundlegender Bedeutung.
Die elementare Verarbeitungseinheit in TFHE-Programmen sieht genau wie ein Neuron aus und besteht aus zwei grundlegenden homomorphen Operationen:
Eine lineare Kombination von Eingaben, die E(x)
zurückgibt, wobei x = w_1 x_1 + … + w_n x_n modulo m
, gegeben die verschlüsselten Eingaben E(x_1), …, E(x_n)
und ein Satz von Klartextgewichten w_1, …, w_n
.
Ein PBS, das E(T[x])
aus E(x)
berechnet, wobei T
eine Klartexttabelle der Größe m
ist.
Im „TFHE-Neuron“ sind m
, x_i
, w_i
sowie die Einträge T[0], …, T[m-1]
alle ganzzahlig, und man kann die „Parameter“ m
, w_1, …, w_n
und T
frei wählen. Da die lineare Kombination nahezu keine Kosten verursacht, ist der Effizienzengpass das PBS, dessen Laufzeit ausschließlich vom Modul m
abhängt: Die Geschwindigkeit nimmt mit zunehmendem m
ab. Dies lädt dazu ein, kleine Werte – ungerade oder gerade, aber mindestens 2 – für Moduli in TFHE-Neuronen zu verwenden, obwohl ein Kompromiss gefunden werden muss, um eine zu drastische Reduzierung ihrer rechnerischen Ausdruckskraft zu vermeiden.
So wie Neuronen in neuronalen Netzwerken in Schichten angeordnet werden, um von Parallelität und HPC-Tricks zu profitieren, stapeln TFHE-Netzwerke Schichten von TFHE-Neuronen. Jede Schicht verfügt über eine Gewichtsmatrix modulo eines gemeinsamen Moduls m
und einen Vektor von Nachschlagetabellen der Größe m
. Der Modul kann jedoch in der vorherigen oder der nächsten Schicht unterschiedlich sein, genau wie die Form der Schicht.
Und damit ist TFHE abgeschlossen! Das war’s, wir müssen nur noch systematisch Funktionen als Nachschlagenetzwerke ausdrücken. Wir haben jetzt unser homomorphes Rechenmodell.
Tatsächlich unterstützt TFHE mehrere Erweiterungen dieses Modells (beliebige Graphen von Operatoren niedrigerer Ebene, Chiffretexte verschiedener Typen, Tabellen-Lookups mit mehreren Ausgaben, mehrere Möglichkeiten zum Packen von Variablen usw.). Aber wir können diese Erweiterungen vorerst ignorieren, da die Vision des Lookup-Netzwerks bereits leistungsstark genug ist, um uns zu ermöglichen, ein einfaches Programm in ein homomorphes Äquivalent umzuwandeln und auszuführen. Wir können uns also darauf konzentrieren, wie wir genau das tun können, zumindest für eine erste Iteration der Technologie.
Ein TFHE-Netzwerk ist daher nur eine Blaupause und noch nicht bereit für die ordnungsgemäße Ausführung in einer homomorphen Anwendung. Obwohl Moduli, Gewichtsmatrizen und Nachschlagetabellen für alle seine Schichten vollständig spezifiziert sind, fehlt ihm immer noch eine entscheidende Komponente, nämlich eine kryptografische Parametrisierung.
Kryptoparameter diktieren jede mögliche Metrik darüber, was zur Laufzeit im Netzwerk passieren wird: konkrete Chiffretextgrößen, die tatsächlichen Tensordimensionen von Key-Switching- und Bootstrapping-Schlüsseln innerhalb von PBSs, verschiedene algorithmische Optionen innerhalb homomorpher Operatoren auf niedriger Ebene, wie sich Klartextpräzisionen und Rauschpegel im gesamten Netzwerk entwickeln und letztendlich die Einzelheiten der Verschlüsselung und Entschlüsselung. Sie sagen auch Speichernutzung und Leistung voraus.
Herauszufinden, welcher Parametersatz die Ausführung eines TFHE-Netzwerks optimiert, kann mühsam und in jedem Fall unerträglich schwierig sein – selbst für Experten –, wenn man es mit Stift und Papier macht wie in den frühen Tagen von FHE, da alles von allem abhängt. Außerdem können mehrere optimale Sätze gleichzeitig koexistieren, was eine Arbitrage zwischen der Größe des öffentlichen Schlüssels, der Latenz des kritischen Pfads oder dem maximalen Durchsatz erfordert. Glücklicherweise wurde das Problem der Automatisierung dieser Aufgabe in den letzten Jahren gelöst und es gibt jetzt leistungsstarke Optimierungstools, um schnell die beste kryptografische Instanziierung eines bestimmten TFHE-Netzwerks zu bestimmen.
Nach der Instanziierung mit kryptografischen Parametern wird ein TFHE-Netzwerk wirklich ausführbar oder, genauer gesagt, durch einen entsprechenden Kompilierungsdurchgang zu einer wirklich ausführbaren Datei zugänglich.
Ein TFHE-Programm ist eine Sammlung parametrisierter TFHE-Netzwerke, die durch „einfache Logik“ zusammengefügt sind.
Einfache Logik besteht aus
Anweisungen, die mit Klartextvariablen (also normalen, nicht verschlüsselten Variablen) arbeiten,
Verzweigungen, entweder unbedingt oder bedingt auf Klartextprädikate,
Speicherlesen und -schreiben an Klartextadressen, Zeigerarithmetik,
Aufrufe von Unterprogrammen/Funktionen.
Im Grunde genommen enthält einfache Logik jede Programmierlogik, die von der Sprache unterstützt wird, mit Ausnahme eines einzigen Falls: das Ändern verschlüsselter Programmvariablen, was das Vorrecht der TFHE-Teile des Programms ist. Das Einzige, was einfache Logik mit Geheimtexten – und TFHE-Public-Keys gleichermaßen – tun darf, ist, sie unverändert zu verschieben und sie den TFHE-Teilen zuzuführen, als ob diese in ihrem eigenen separaten Coprozessor oder Container laufen würden.
Ein Programm, das diese Definition erfüllt, ist im Grunde genommen vollständig und kann zu einer vollwertigen homomorphen Anwendung werden, unabhängig von der Programmiersprache. Eine benutzerdefinierte Kompilierung stellt diese endgültige Zuordnung bereit und das resultierende Objekt kann dann auf einer TFHE-fähigen Laufzeitumgebung oder als eigenständige, in sich geschlossene ausführbare Datei ausgeführt werden.
Um die Darstellung von TFHE-Programmen zu vereinheitlichen, kann eine dedizierte Sprache verwendet werden – etwa einige DSLs oder besser ein MLIR-Dialekt –, sodass die Kompilierung mit demselben Back-End-Compiler durchgeführt werden kann.
Die genaue Art der Laufzeit (gemeinsam genutzte/dynamische Bibliothek, VM oder etwas anderes) ist hier nur eine Modalität. Beide Optionen führen zu einer funktionsfähigen homomorphen App auf TFHE-Basis, die bereitgestellt und Benutzern zugänglich gemacht werden kann.
Kommen wir nun für eine Minute zum eigentlichen Spiel zurück.
Wir haben es mit einem Entwickler zu tun, der nichts von TFHE oder dem oben genannten weiß, aber eine homomorphe Anwendung erstellen möchte. Nehmen wir an, wir haben den oben besprochenen Compiler und die TFHE-fähige Laufzeitumgebung, falls vorhanden, freigegeben.
Unser Ziel steht fest, wir brauchen nur noch ein TFHE-Programm, um eine ausführbare Datei zu erhalten. Aber ... wie zum Teufel bringen wir den Entwickler überhaupt dazu, etwas so Spezifisches wie ein TFHE-Programm selbst zu erstellen?
Hier kommt der einfache Weg zum Erfolg: Sie kapseln die gesamte Komplexität in einer Black-Box-FHE-API.
In jeder Programmiersprache kann ein (einfaches) Programm im Wesentlichen als eine Kombination aus drei Bestandteilen betrachtet werden:
die programmatische Logik, bestehend aus spracheigenen Anweisungen und Scoping-Konstrukten,
Variablen und eine Auswahl von Datentypen, die das Programm ihnen zuweist, ausgewählt aus allen möglichen Datentypen,
Aufrufe einer Auswahl externer Funktionen, die aus allen verfügbaren externen Funktionen ausgewählt werden, die das Programm zur Bearbeitung seiner Variablen verwendet.
Datentypen haben ihre eigene Untersprache, eine Mischung aus nativen Typen und Typkonstrukten, um diese nativen Typen zu erweitern und sie zu strukturierten Typen höherer Ebene zu kombinieren. Das Typsystem soll genügend Ausdruckskraft bieten, um praktisch jede Datenstruktur abzudecken, die das Programm benötigt. Externe Funktionen sind diejenigen, die von Bibliotheken bereitgestellt werden, ob Standard- oder andere, sie können aber auch implizit durch sprachnative Anweisungen aufgerufen werden (denken Sie an Operatoren für modulare Arithmetik oder Division).
Einfache Programme sind also tatsächlich „einfach“:
Hören Sie mir gut zu. Ich sage nicht, dass alle hochrangigen Programmierkonzepte wie Polymorphismus, Objekte mit ihren Mitgliedern und Attributen, Klassen und hierarchische Vererbung, Vorlagen, Makros, Merkmale, Threads, Rekursion, syntaktischer Zucker, Anmerkungen und alle anderen denkbaren kostenlosen Abstraktionen, die die Sprache bietet, für den Entwickler unbedingt einfach zu handhaben sind, obwohl sie erfunden wurden, um seine Arbeit zu vereinfachen.
Ich sage nur, dass es sich für unseren Zweck nur um harmlose Dekorationen handelt, die zur Kompilierzeit verschwinden, weil aus der Quelle eine abgespeckte, aber äquivalente Version des Programms in Normalform abgeleitet wird. Diese Version des Programms, ausgedrückt in einer beliebigen Zwischendarstellung (IR), besteht aus geradlinigen Basisblöcken – Anweisungsfolgen, die in dieser IR elementar sind –, die durch ein Kontrollflussdiagramm verbunden sind.
Nun ist es dieses Programm in Normalform, das „einfach“ ist. Ich meine, einfach genug, um mit homomorphen Fähigkeiten erweitert zu werden.
Sie gewinnen das Spiel, indem Sie eine sprachnative FHE-Bibliothek veröffentlichen und den Entwickler entscheiden lassen, wie diese am besten genutzt werden kann.
Die Bibliothek wird neue Datentypen bereitstellen – verschlüsselte, als Ergänzung zu den einfachen – und eine Sammlung homomorpher Funktionen, die (mehr oder weniger) die einfachen Funktionen nachahmen, mit denen der Entwickler vertraut ist, nur dass sie mit verschlüsselten Datentypen arbeiten. Kurz gesagt, Sie erweitern das Typsystem und das Bibliotheksökosystem und überlassen es der Intelligenz des Entwicklers, herauszufinden, wie er diese Erweiterungen nutzen kann, um seine homomorphe App zu erstellen.
Dies ist nicht speziell an TFHE gebunden, jedes FHE-Schema ist geeignet. Darauf konzentrieren sich die Anbieter der verschiedenen FHE-Bibliotheken: Verbesserung der Nutzbarkeit und Benutzerfreundlichkeit von FHE durch Bereitstellung hochrangiger homomorpher Funktionen, die in Aussehen und Verhalten dem einfachen Codierungserlebnis so nahe wie möglich kommen. Die gesamte kryptografische Komplexität wird in Blackboxen abstrahiert, an die das Programm Oracle-Aufrufe sendet.
Für den Entwickler kann das natürlich gut funktionieren. Zumindest, wenn Sie als Bibliotheksanbieter Ihren Teil der Abmachung einhalten.
Sie werden ein homomorphes Programm entwickeln, das jetzt ungefähr so aussieht.
Es gibt jetzt sowohl einfache als auch verschlüsselte Variablen, und das Programm muss eine strikte Trennung zwischen diesen beiden Objekttypen aufrechterhalten. Das liegt daran, dass es diese goldene FHE-Regel gibt, die besagt, dass, wenn Sie eine Funktion auf eine Mischung aus einfachen und verschlüsselten Argumenten anwenden, das Ergebnis zwangsläufig verschlüsselt ist, z. B. gibt fhe_add(E(x), y)
E(x+y)
zurück und so weiter. Einfache Variablen können also in einige FHE-Funktionen eintreten, aber nie aus ihnen herauskommen. Homomorphe Verschlüsselung „kontaminiert“ durch Berechnungen alles, was sie berührt.
Sehen Sie also, wie verzweigen Sie dann bedingt zu einer verschlüsselten Variable?
Nun, das können Sie nicht. Aber das ist überhaupt kein großes Problem.
In einer FHE-Anwendung kann bedingte Verzweigung nur auf einfache Boolesche Werte angewendet werden, nicht auf verschlüsselte. Woher wissen Sie anhand eines verschlüsselten Bits, wohin Sie springen müssen? Sie haben nicht den privaten Schlüssel des Benutzers, um dieses Bit zu entschlüsseln.
Glücklicherweise gibt Ihnen FHE auch einfache Tricks an die Hand, um hier Abhilfe zu schaffen.
Angenommen, der Entwickler wollte ursprünglich etwas tun wie
if (x == 0) then y = 3 else z = 7
erkennt aber, dass die Variable x
zu diesem Zeitpunkt tatsächlich verschlüsselt sein wird. Wie kann dieser Codeabschnitt angepasst werden?
Als Erstes müssen Sie die einfache if
Anweisung überarbeiten, um einen entsprechenden geradlinigen Codeabschnitt zu erhalten, der Multiplexing verwendet:
bit = (x == 0) // bit = 1 if x == 0 otherwise 0 y = 3 * bit + y * (1 - bit) // y = 3 if bit == 1 otherwise no change z = z * bit + 7 * (1 - bit) // z = 7 if bit == 0 otherwise no change
In einem zweiten Durchgang muss der Entwickler die Tatsache, dass x
ein verschlüsselter Typ ist, auf die folgenden Zeilen übertragen:
bit = fhe_is_equal(x, 0) // bit, y_new and z_new are encrypted y_new = fhe_add(fhe_mul(3, bit), fhe_mul(y, fhe_sub(1, bit))) z_new = fhe_add(fhe_mul(z, bit), fhe_mul(7, fhe_sub(1, bit)))
Man kann überprüfen, ob dies funktional der ursprünglichen Absicht des Entwicklers entspricht, und das war’s.
Wenn die Programmiersprache das Überladen nativer Operatoren zulässt, kann die FHE-API sogar die expliziten FHE-Funktionen des zweiten Snippets überflüssig machen, sodass das erste Umschreiben das Einzige ist, was der Entwickler tun muss.
Um dem Entwickler eine Extraportion syntaktischen Zuckers zu geben, können Sie sogar einen überladenen ternären Operator a? b : c
bereitstellen, wobei jedes Argument verschlüsselt sein kann oder nicht. Der Codeabschnitt wird dann noch einfacher:
bit = (x == 0) // bit = 1 if x == 0 otherwise 0 y_new = bit? 3: y // y = 3 if bit == 1 otherwise no change z_new = bit? z: 7 // z = 7 if bit == 0 otherwise no change
Dies lässt sich leicht auf beliebige if/switch
-Anweisungen verallgemeinern: Jedes Mal, wenn eine verschlüsselte Bedingung zu prüfen ist, muss der Entwickler lediglich Multiplexing verwenden, um die verschiedenen Hauptteile der Anweisung in einem einzigen Block äquivalenten geradlinigen Codes zusammenzufügen.
Nun können Schleifenkonstrukte mit verschlüsselten Bedingungen im gleichen Sinne reguliert werden. Nehmen wir zum Beispiel
for (i = 0; i < x; i++) do <body> // i is plain, x is encrypted
wobei x
ein verschlüsselter Typ sein soll. Zerlegen Sie dies zunächst in eine einfache for
Anweisung und eine unregelmäßige if
-Anweisung:
for (i = 0; i < known_max_value_of_x; i++) do if (i < x) then <body> // i is plain, x is encrypted
und regulieren Sie dann die if
Anweisung wie zuvor:
for (i = 0; i < known_max_value_of_x; i++) do bit = (i < x) // i is plain, x and bit are encrypted <new_body> // new body regularized using bit? _ : _
Beachten Sie, dass hierfür ein unmittelbarer Wert known_max_value_of_x
erforderlich ist. Der vom verschlüsselten Typ von x
unterstützte Maximalwert kann standardmäßig verwendet werden, aber in vielen Fällen kennt der Entwickler eine viel bessere Obergrenze für x
, die es ermöglicht, die Gesamtzahl der Schleifen auf ein striktes Minimum zu reduzieren.
Letztendlich lassen sich die obigen Transformationen leicht zu einer systematischen Methode verallgemeinern, mit der unregelmäßige Kontrollflüsse reguliert werden können, was für Programmierer leicht zu erlernen und in ihre Programmiergewohnheiten zu integrieren ist.
Zamas fhEVM ist ein vollwertiges Open-Source-Framework für die Entwicklung und Bereitstellung vertraulicher Smart Contracts auf der Ethereum Virtual Machine (EVM). fhEVM-Verträge sind einfache Solidity-Verträge, die mit herkömmlichen Solidity-Toolchains erstellt werden. Die vertraute Entwicklererfahrung wird durch die Bibliothek TFHE.sol
, die verschlüsselte Datentypen und FHE-Ersetzungen für Standardfunktionen bereitstellt, FHE-erweitert.
Die unterstützten verschlüsselten Datentypen sind derzeit
ebool, euint4, euint8, euint16, euint32, euint64, eaddress
und verschlüsselte vorzeichenbehaftete Ganzzahlen werden bald ebenfalls enthalten sein. Verschlüsselte Variablen werden aus Roheingabe-Chiffretexten mithilfe dedizierter Konstruktoren erstellt:
function mint(bytes calldata encryptedAmount) public onlyContractOwner { euint64 amount = TFHE.asEuint64(encryptedAmount); balances[contractOwner] = balances[contractOwner] + amount; totalSupply = totalSupply + amount; }
Die nativen Operatoren von Solidity +, -, *, &, |, ^, etc
sind zur Vereinfachung für Entwickler überladen , und die bereitgestellten Vergleichsoperatoren eq, ne, gt, lt, ge, le
geben einen verschlüsselten Booleschen ebool
zurück. Der homomorphe ternäre Operator _? _ : _
wird select
genannt:
function bid(bytes calldata encryptedBid) internal { euint32 bid = TFHE.asEuint32(encryptedBid); ebool isAbove = TFHE.le(bid, highestBid); // Replace highest bid highestBid = TFHE.select(isAbove, bid, highestBid); }
Auf der Laufzeitseite bietet fhEVM ein TFHE-fähiges EVM, in dem homomorphe Funktionen als vorkompilierte Verträge verfügbar gemacht werden, was aus der Integration der Open-Source-Rust-Bibliothek TFHE-rs
resultiert.
Weitere Informationen hierzu finden Sie im Whitepaper von fhEVM .
Erinnern Sie sich, wie ausführbare TFHE-Programme aussehen, parametrisierte TFHE-Netzwerke, die mit einfacher Logik zusammengesetzt sind? Nun, Sie brauchen nur eine Möglichkeit, die Softwarelogik des Entwicklers darauf abzubilden.
Die erste Anforderung besteht darin, sicherzustellen, dass die Programmlogik „einfach“ ist. Genau das haben wir den Entwicklern beigebracht, indem sie ihre Kontrollflussanweisungen regulieren. Damit sind wir jetzt eigentlich gut aufgestellt.
Die zweite Anforderung besteht darin, dass alle vom Programm aufgerufenen homomorphen Funktionen auf vordefinierte parametrisierte TFHE-Netzwerke abgebildet werden müssen. Dies ist aus mehreren Gründen komplizierter, als es aussieht.
Die Vorabeinrichtung eines parametrisierten TFHE-Netzwerks, das eine bestimmte Funktion implementiert, ist nicht unbedingt trivial.
Allein das Erstellen einer homomorphen Addition von zwei verschlüsselten 64-Bit-Ganzzahlen ohne Vorzeichen führt zu zahlreichen technischen Optionen: Wie stellen Sie die 64-Bit-Eingaben als Vektoren modularer Ganzzahlen dar? Mit welchem Modul (oder mehreren Modulen) genau? Und wie realisieren Sie dann eine 64-Bit-Additionsschaltung mit mehreren Schichten von Tabellennachschlagevorgängen?
Es gibt viele Möglichkeiten. Aber Sie werden sich letztendlich durch gute Ingenieursleistung und viele Experimente entscheiden.
Angenommen, Sie haben TFHE-Netzwerke für alle Funktionen der API implementiert, möchten Sie sicherstellen, dass sie wie Legosteine beliebig zusammengesetzt werden können.
Dies ist nicht unbedingt garantiert, da die beste Darstellungsmethode für denselben verschlüsselten Datentyp von Funktion zu Funktion unterschiedlich sein kann. Sie müssen also ein gemeinsames Rechenformat verwenden, um jeden verschlüsselten Datentyp darzustellen, ohne die Effizienz Ihrer TFHE-Netzwerke zu sehr zu beeinträchtigen.
Auch hier gibt es viele Optionen und Sie müssen eine Arbitrage zwischen ihnen durchführen.
Unter der Annahme, dass alle TFHE-Netzwerke mittlerweile hinsichtlich ihrer Eingabe-/Ausgabeformate vollständig kompatibel sind, ist die Zusammensetzbarkeit möglicherweise immer noch nicht gewährleistet.
Dies liegt daran, dass die kryptografischen Parameter, die ein TFHE-Netzwerk instanziieren, möglicherweise nicht mit denen kompatibel sind, die zum Instanziieren eines anderen verwendet werden. Insbesondere muss der Pegel des Verschlüsselungsrauschens innerhalb der Eingabe- und Ausgabe-Chiffretexte angepasst werden, um die tatsächliche Zusammensetzbarkeit sicherzustellen.
Das ist ähnlich wie die Impedanz in elektronischen Schaltkreisen. Es gibt keine Möglichkeit, einen Schaltkreis mit einem anderen zu verbinden, wenn die Impedanz nicht übereinstimmt. Sie müssen zuerst ihre Impedanzpegel angleichen, und hier ist es dasselbe. Der einfachste Weg, dies zu tun, besteht darin, feste Parametersätze zu verwenden – vielleicht sogar nur einen einzigen Satz –, die so abgestimmt sind, dass die Ausrichtung über alle API-Funktionen hinweg sichergestellt ist. Anschließend wird das Format der öffentlichen Benutzerschlüssel festgelegt, ebenso wie die Parameter, die bei der Benutzerverschlüsselung und -entschlüsselung verwendet werden, unabhängig von den Entwicklercodes.
Wenn Sie beim Erstellen und Parametrisieren Ihrer TFHE-Netzwerke einen Weg finden, diese 3 Anforderungen zu erfüllen und trotzdem eine gute Gesamtleistung zu erzielen, dann herzlichen Glückwunsch! Sie haben es geschafft.
Sie sind gut genug für die allgemeine Programmierung, vorausgesetzt, dass die FHE-API umfassend genug ist, um homomorphe Ersetzungen für alle vom Entwickler erwarteten Standardfunktionen mit vollständiger Zusammensetzbarkeit bereitzustellen.
Aber sie sind möglicherweise nicht gut genug für Programme, die sich spezialisieren auf
große, rechenintensive Funktionen wie beim maschinellen Lernen,
benutzerdefinierte, nicht standardmäßige Funktionen.
Hier kommt die homomorphe Kompilierung ins Spiel.
Hier beginnt der harte Weg: Um das Spiel außerhalb der Allzweckprogrammierung zu gewinnen, liefern Sie jetzt einen TFHE-Compiler.
Der Compiler kümmert sich um das, was der Entwickler nicht selbst zu tun weiß. Er wird mit den Eingaben des Entwicklers gespeist – was auch immer das sein mag, Sie entscheiden – und muss die fehlenden Teile vervollständigen, um ein TFHE-Programm zu erhalten.
Schauen wir uns typische Beispiele für nicht standardmäßige Anwendungen an.
Indem der Entwickler ein einfaches neuronales Netzwerk in ein homomorphes Äquivalent umwandelt, kann er einen homomorphen Inferenzdienst erstellen und bereitstellen, bei dem Benutzereingaben und -ausgaben Ende-zu-Ende-verschlüsselt sind.
Der Entwickler sollte sich im maschinellen Lernen gut genug auskennen, um ein trainiertes quantisiertes Modell zu erstellen oder bereits über eines zu verfügen.
Die Einzelheiten der Quantisierung sind hier wirklich wichtig, da Ihr Compiler erfordert, dass das Modell im Grunde ein TFHE-Netzwerk ist – oder durch einfaches Umschreiben leicht in eines umgewandelt werden kann. Verfügbare Open-Source-Techniken unterstützen diese Form der Quantisierung bekanntermaßen, entweder durch nachträgliche Quantisierung eines vorab trainierten Modells oder vorzugsweise durch Quantization-Aware Training (QAT), das im Vergleich zu nicht quantisierten Modellen, die mit demselben Datensatz trainiert wurden, modernste Genauigkeitsgrade erreicht.
Im Wesentlichen sind die in den TFHE-Netzwerkschichten verwendeten Moduli variable Potenzen von 2, sodass die Genauigkeit der Aktivierungssignale in Bits gemessen wird. Gewichte sind vorzeichenbehaftete Ganzzahlen und Aktivierungsfunktionen selbst werden quantisiert und als Tabellennachschlagevorgänge instantiiert. Wenn Aktivierungen eine verschobene Funktion mit hartem Vorzeichen und einem gelernten Offset tabellieren, umfasst diese Definition Modelltypen wie BNNs , TNNs und ihre Multi-Bit-Generalisierungen. Aber im Prinzip kann man beliebige Nachschlagetabellen in Aktivierungsfunktionen verwenden, und daher könnten diese sogar während des Trainings gelernt werden, um bessere Genauigkeiten zu erreichen.
Der Entwickler weiß jedoch nicht, wie er sein TFHE-Netzwerk in eine homomorphe ausführbare Datei umwandeln soll. Die einzige fehlende Zutat ist also eine kryptografische Parametrisierung dieses Netzwerks. Das ist alles, was Ihr Compiler tun muss, bevor er mit der Back-End-Kompilierungsphase fortfahren kann.
Denken Sie daran, dass die Parametrisierung eines TFHE-Netzwerks eine kryptografische Instanziierung bereitstellt, die ausgeführt werden kann. Sie steuert auch alle Metriken, die sich auf diese Ausführung beziehen, wie die Gesamtgröße der öffentlichen Benutzerschlüssel und die Gesamtlaufzeit. Daher ist die Parametrisierung hier von entscheidender Bedeutung, da der Entwickler versucht, die Latenz der Inferenz auf ein absolutes Minimum zu reduzieren.
Es gibt viel zu viele Freiheitsgrade in den kryptografischen Parametern eines TFHE-Netzwerks, um sie alle mit roher Gewalt zu erzwingen. Außerdem hängen die zu optimierenden Metriken von zwei Komponenten ab, die völlig außerhalb des Netzwerks liegen und von den Algorithmen abhängen, die die Laufzeit verwendet, um TFHE-Operationen auf niedriger Ebene durchzuführen:
Eine Sammlung von Rauschformeln . Eine Rauschformel setzt die Eingangs- und Ausgangsverteilungen des Verschlüsselungsrauschens an den Endpunkten eines Operators in Beziehung, wobei die Parameter des Operators als unbekannte Variablen verwendet werden. Ihre Erstellung erfordert eine wissenschaftliche Analyse durch den Menschen und eine experimentelle Validierung.
Eine Sammlung von Kostenmetriken . Kostenmetriken sagen die mehrdimensionale Effizienz (Speichernutzung, Laufzeit usw.) eines Operators als Funktion seiner Parameter voraus. Sie werden normalerweise aus Benchmarkmessungen durch eine Best-Fit-Analyse abgeleitet.
Jede Änderung in der Implementierung der Laufzeit muss sich in diesen beiden Modulen widerspiegeln, da sie beide stark algorithmus- und hardwareabhängig sind.
Die Rauschformeln und das Kostenmodell der Laufzeit formulieren zusammen mit dem gegebenen TFHE-Netzwerk eine spezifische Instanz einer ganzen Klasse von Optimierungsproblemen, und diese Instanz wird an einen dedizierten Optimierer übergeben. Wir sprechen hier von gemischt ganzzahliger nichtlinearer Programmierung mit mehreren Zielen. Um also eine Pareto-Front optimaler Parametersätze zu finden, muss diese nicht triviale Klasse von Optimierungsproblemen gelöst werden.
Dank fundierter wissenschaftlicher und technischer Erkenntnisse konnten Optimierungsprobleme dieser Art in Sekundenschnelle gelöst werden und TFHE-Compiler wie Concrete verfügen bereits über einen effizienten TFHE-Parameteroptimierer als internes Modul.
Verschiedene Verbesserungen können TFHE-Optimierer in Zukunft noch schneller machen, aber auch ohne diese kann man die Parametrisierung von TFHE-Netzwerken als so gut wie beschlossene Sache betrachten.
Diese Anwendungen sind von ganz anderer Art. Der Entwickler besitzt die mathematische Spezifikation einer zu implementierenden benutzerdefinierten Funktion zusammen mit einer Präzisionsbeschränkung. Nehmen wir zum Beispiel
wobei x
eine reelle Zahl zwischen 0 und 1 ist und die Verwendung einer Näherung G
von F
akzeptabel ist, solange
Der Entwickler weiß, dass die Implementierung von G
auf der Rückseite eines Umschlags durch die Zusammenstellung von Standard-API-Funktionen wahrscheinlich viel zu suboptimal ist.
Ihr Compiler erstellt im Handumdrehen ein neues TFHE-Netzwerk, das speziell auf die Spezifikation von G
zugeschnitten ist. Anschließend parametrisiert er es und fährt mit der Back-End-Kompilierung fort, um die homomorphe App zu erstellen.
Nun, da wird die Straße plötzlich viel holpriger.
Derzeit fehlt es an wissenschaftlichen Erkenntnissen darüber, wie TFHE-Netzwerke, mit der genauen Definition, die ich zuvor angegeben habe, automatisch synthetisiert werden können. Sogar Forschungen zur Synthese ähnlicher Schaltungstypen, die ebenfalls auf ganzzahliger modularer Arithmetik basieren, sind bestenfalls spärlich. Ganz zu schweigen von einem bereits existierenden Synthesizer, der diese Aufgabe von A bis Z ausführen kann.
Eine Möglichkeit, das Problem zu lösen, besteht darin, einen Bruchteil der Rechenleistung von TFHE-Netzwerken auszunutzen, indem man sie auf Boolesche Schaltkreise reduziert. Beispielsweise kann ein TFHE-Neuron gezwungen werden, als ternäres Boolesches Gatter zu fungieren
von
x_1, x_2, x_3
als 0/1-Werte festlegen,m = 4
und seiner Gewichte auf (w_1, w_2, w_3) = (2, -1, 1)
,[0, 1, 1, 0]
.
Durch Brute-Forcing aller möglichen Gewichte und Tabellen mit demselben Modul kann man dann ein Wörterbuch von TFHE-Neuronen erstellen, das zur Berechnung ternärer Gatter dienen kann. Der nächste Schritt besteht darin
Dies ist nur eine von vielen illustrativen Strategien, da es viele Methoden gibt, die auf Booleschen Schaltkreisen basieren. Sie können auch einfach die üblichen binären Gatter in Betracht ziehen und diese mit eingeschränkten TFHE-Neuronen implementieren. CEAs Cingulata und – später – Googles FHE-Transpiler haben mit TFHE genau diesen Weg beschritten.
Bei der Booleschen Synthese kommen aggressive Techniken zur Schaltungsoptimierung zum Einsatz, und das technische Problem ist insgesamt gelöst – oder zumindest so gut wie gelöst –, sodass dieser Ansatz für denjenigen, der den Compiler erstellt, sinnvoll und praktikabel ist.
Wenn jedoch ein TFHE-Netzwerk auf diese Weise generiert wird, kann seine Breite und Tiefe ungewöhnlich hoch sein, was zu einer schlechten Gesamtleistung führt. Daher besteht der weit verbreitete Verdacht, dass man durch Lockerung der - völlig künstlichen - booleschen Konditionierung von TFHE-Neuronen ihre volle Ausdruckskraft nutzen und viel kleinere, viel flachere Netzwerke erhalten kann.
Aber auch hier bedarf es weiterer Forschung, um eindeutig zu ermitteln, wie das zu bewerkstelligen ist. Es ist möglich, dass völlig andere Ansätze, wie das Lernen des TFHE-Netzwerks mit einer geeigneten Trainingsmethode aus dem maschinellen Lernen, letztendlich bessere Ergebnisse liefern. Die Zeit wird es zeigen.
Vorausgesetzt, unser Syntheseproblem ist gelöst und führt zu effizienten, benutzerdefinierten TFHE-Netzwerken, wären Sie bereit, alle beweglichen Teile zusammenzusetzen und einen Compiler zu entwerfen, der den ganzen Kram erledigt:
Als Eingabe würde ein einfaches Programm verwendet, in dem sensible Variablen lediglich als verschlüsselt gekennzeichnet würden.
Es würde vortrainierte neuronale Netzwerke oder andere Modelle des maschinellen Lernens akzeptieren und sie als TFHE-Netzwerke neu interpretieren.
Es würde Funktionsmodelle akzeptieren, die nur auf der Grundlage einer mathematischen Spezifikation erstellt wurden, und würde eine Synthese im laufenden Betrieb durchführen, um benutzerdefinierte TFHE-Netzwerke zu generieren.
Es würde alle nicht parametrisierten TFHE-Netzwerke, die in der Kompilierungseinheit hängen bleiben, bei Bedarf mithilfe eines Optimierungsmoduls in ausführbare Instanzen umwandeln.
Es würde die Back-End-Phase der Kompilierung für eine Vielzahl von TFHE-Ziellaufzeiten und/oder Hardwarearchitekturen durchführen.
Es würde die Vorteile bestimmter Hardwarebeschleuniger (über ihr Kostenmodell) nutzen, um die Synthese und Parametrisierung schnellerer TFHE-Netzwerke zu ermöglichen.
Verdammt, man könnte genauso gut Unterstützung für eine automatische Regularisierung des Kontrollflusses einbauen, sodass sich der Entwickler nicht einmal mehr darum kümmern muss.
Dies würde den FHE-App-Entwicklern das ultimative Entwicklungserlebnis bieten.
Im Kontext der allgemeinen FHE-Programmierung kann eine TFHE-Bibliothek Modularität und eine vollständig autonome Entwicklungserfahrung mit bereits vorhandenen Toolchains bieten.
TFHE ist Vorreiter bei der Entwicklung spezifischer Kompilierungstechniken, die die Anforderungen der Entwickler darüber hinaus erfüllen können, insbesondere im Bereich der verschlüsselten Inferenz beim maschinellen Lernen und in den kommenden Jahren im Bereich der Hochgeschwindigkeitsverarbeitung verschlüsselter Daten und anderer benutzerdefinierter FHE-Anwendungen.
Insgesamt bietet TFHE einen klaren Technologiepfad zur Schaffung besser integrierter und adaptiver FHE-Toolchains, die in der Welt der Softwareentwicklung groß rauskommen können und eine neue Welle datenschutzorientierter Lösungen hervorbringen, die jeder mit beispielloser Leichtigkeit erstellen und ausführen kann.
Indem ich mich ausschließlich auf TFHE-Suchnetzwerke konzentriert habe, habe ich nur ein Rechenmodell unter mehreren verwendet, die TFHE unterstützen kann. Da die Forschung nach und nach immer mehr seiner Fähigkeiten freilegt, werden zweifellos neue Möglichkeiten zur Instrumentierung von TFHE an die Oberfläche kommen.
Welche und wann das sein werden, ist eine andere Geschichte. Doch dahinter verbergen sich eine ganze Reihe weiterer spannender und möglicherweise aufschlussreicher Fragen zur Zukunft des vertraulichen Computing.