paint-brush
Chaînes d'outils FHE de nouvelle génération pour le multivers de développement : comment TFHE nous y emmènepar@pascalpaillier
1,235 lectures
1,235 lectures

Chaînes d'outils FHE de nouvelle génération pour le multivers de développement : comment TFHE nous y emmène

par Pascal Paillier23m2024/05/17
Read on Terminal Reader
Read this story w/o Javascript

Trop long; Pour lire

Cet article explore diverses stratégies pour concevoir la prochaine génération de chaînes d’outils FHE en pariant sur TFHE. L’état actuel des connaissances sur la manière d’instrumenter du code homomorphe avec TFHE est déjà suffisant pour créer de tels outils dès maintenant et les mettre à la disposition des développeurs, leur permettant ainsi d’intégrer facilement l’informatique confidentielle lors de la création d’applications.

People Mentioned

Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coins Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Chaînes d'outils FHE de nouvelle génération pour le multivers de développement : comment TFHE nous y emmène
Pascal Paillier HackerNoon profile picture

Introduction

FHE change la donne et nous sommes tous invités à jouer.


Si vous n'avez aucune idée de ce dont je parle, cela signifie que vous vivez sous un rocher ces derniers temps. Allez parcourir certaines des ressources complètes de FHE.org et revenez.


Pour que FHE puisse tenir sa promesse envers le monde de la technologie, il doit s'accompagner d'une nouvelle génération d'outils de puissance industrielle pour le développement, la compilation et l'exécution, que tout le monde peut utiliser pour créer facilement des applications homomorphes.


Cependant, à l’heure actuelle, de nombreux experts et entreprises du secteur FHE investissent encore la plupart de leur temps et de leurs efforts dans l’amélioration de la cryptographie derrière FHE, au lieu de se concentrer sur la création d’excellents outils de développement pour les non-experts. Écoutez-moi bien ici : l’amélioration des fonctionnalités et des performances de base de FHE sera toujours une bonne nouvelle. Mais dans l’ensemble, ces améliorations progressives ne favorisent, au mieux, que marginalement l’adoption mondiale. Ils auront un impact sur l'adoption à un moment donné, mais pas maintenant .


De mon point de vue, il est évident que le monde de la technologie a aujourd'hui besoin de chaînes d'outils FHE puissantes et conviviales pour les développeurs, afin de commencer à débloquer des réussites alimentées par FHE et de faire passer FHE d'une tendance technologique à un véritable changement de paradigme dans le secteur de la sécurité numérique. Je crois que l'état actuel des connaissances sur le FHE - tant sur le plan scientifique que technologique - est déjà suffisant pour construire de tels outils dès maintenant et les mettre à la disposition des masses férues de technologie sans plus tarder. L’intégration continue de nouvelles fonctionnalités se déroulera naturellement au fil du temps, comme c’est toujours le cas.


Mais voilà : le FHE se décline en de nombreuses saveurs. Selon le schéma cryptographique que vous utilisez - ou l'usage particulier que vous en faites, il existe une manière différente de représenter le calcul et d'exécuter des programmes homomorphes. C'est comme si les schémas FHE étaient des animaux complètement différents, l'un vous donnant des machines de Turing et un autre un calcul lambda. La biodiversité est toujours bonne en technologie ou autre, mais cela signifie également que vous devez définir une stratégie viable lors de l'instrumentation FHE dans la pratique.


Mon entreprise Zama se concentre sur un programme FHE particulier qui est le TFHE . TFHE réalise un traitement de données homomorphe avec des atouts très spécifiques : des bootstraps ultra-rapides et des calculs exprimés sous forme de réseaux de recherches de tables. Nous avons acquis une compréhension approfondie de la façon dont ces particularités - qui faisaient du TFHE une sorte d'opprimé dans l'espace FHE - peuvent se traduire en bibliothèques homomorphes, en compilation, en machines virtuelles ou en accélération matérielle.


D'autres concurrents importants du FHE comme CKKS , BGV ou BFV impliquent des concepts très différents dans leur instrumentation pratique : les bootstrappings sont trop lents pour être une option, le traitement est donc limité en profondeur, cependant les données peuvent être vectorisées avec un traitement par lots massif, des calculs exprimés sous forme de circuits polynomiaux et - dans le cas de CKKS - les résultats sont approximatifs. Ainsi, la traduction de BGV/BFV et CKKS en compilateurs et runtimes nécessite un état d’esprit totalement différent de la part des constructeurs d’outils.


Cependant, il est peu probable que les développeurs se soucient beaucoup du schéma particulier qui alimente leur chaîne d'outils FHE et leurs environnements d'exécution, tant qu'ils sont faciles à utiliser et que les exigences de performances sont respectées dans les applications homomorphes déployées. Ils sont eux-mêmes des bâtisseurs créatifs et doivent se concentrer sur les nouvelles expériences qu’ils peuvent offrir à leurs utilisateurs.


L’objectif final pour les acteurs de la technologie FHE est donc de concevoir et de fournir des outils qui soient non seulement prêts à être utilisés en prime time dans le multivers de développement, mais suffisamment puissants pour établir une nouvelle norme industrielle. Pour y parvenir, ils doivent tenter leur chance et déterminer quel programme FHE est le plus susceptible de les y amener.


Jouons à un jeu.


Prenez un développeur qui ne connaît rien aux subtilités de FHE mais souhaite créer une application homomorphe. Vous êtes le constructeur d'outils ici, vous faites face à ce développeur, dont vous pouvez vous attendre à une certaine familiarité avec les pratiques de développement courantes et les bases de l'informatique, mais tout le reste - les mathématiques avancées et autres, est interdit. Comment réussir à leur faire produire eux-mêmes l’application FHE ?


Cet article explore différentes stratégies pour gagner ce jeu en pariant sur TFHE.


En fonction de la nature de l'application (analyse de données personnalisée, réseaux neuronaux, contrats intelligents ou programmation à usage général), des voies gagnantes compatibles TFHE seront explorées. Cette exploration nous mènera sur une route facile, une route difficile et quelques autres entre les deux, avec différents niveaux de préparation technologique dans leur réalisation pratique.

Que sont les programmes TFHE ?

TFHE signifie Torus FHE. Également appelé CGGI d'après les noms de ses découvreurs, TFHE occupe une position unique dans le paysage FHE : il s'agit du mécanisme le plus connu permettant le bootstrapping programmable (PBS).


En un mot, un PBS est une recherche de table homomorphe. Il renvoie un cryptage de T[x]T est une fonction tabulée de votre choix, étant donné un cryptage d'un index x . Sa vitesse d'exécution ne dépend pas des entrées de T mais uniquement du nombre d'entrées, et est de l'ordre de la milliseconde. De plus, un PBS réinitialise le bruit de chiffrement intégré dans son texte chiffré de sortie, de sorte que vous pouvez composer des PBS en séquence indéfiniment, sachant que votre application homomorphe gérera toujours des textes chiffrés propres.

Réseaux TFHE

Le modèle informatique préconisé par TFHE est la quintessence.


L'unité élémentaire de traitement des programmes TFHE ressemble exactement à un neurone et compose 2 opérations homomorphes de base :


  1. Une combinaison linéaire d'entrées qui renvoie E(x)x = w_1 x_1 + … + w_n x_n modulo m , étant donné les entrées chiffrées E(x_1), …, E(x_n) et un ensemble de poids de texte en clair w_1, …, w_n .


  2. Un PBS qui calcule E(T[x]) à partir de E(x)T est une table en texte brut de taille m .

Un neurone TFHE


Dans le "neurone TFHE", m , x_i , w_i , ainsi que les entrées T[0], …, T[m-1] sont tous des nombres entiers, et on peut choisir librement les "paramètres" m , w_1, …, w_n et T . Étant donné que la combinaison linéaire a un coût quasi nul, le goulot d'étranglement en termes d'efficacité est le PBS dont le temps de fonctionnement dépend uniquement du module m : la vitesse diminue à mesure que m augmente. Cela invite à utiliser de petites valeurs - impaires ou paires, mais au moins 2 - pour les modules des neurones TFHE, même s'il faut trouver un compromis pour éviter une réduction trop drastique de leur expressivité computationnelle.


Désormais, de la même manière que les neurones sont assemblés en couches dans les réseaux de neurones pour bénéficier du parallélisme et des astuces HPC, les réseaux TFHE empilent des couches de neurones TFHE. Chaque couche comporte une matrice de poids modulo un module commun m et un vecteur de tables de recherche de taille m . Cependant, le module est libre de différer dans la couche précédente ou suivante, tout comme la forme de la couche.


Un réseau TFHE


Et voilà, TFHE est terminé ! Et voilà, il suffit d’exprimer systématiquement les fonctions sous forme de réseaux de recherche. Nous avons maintenant notre modèle informatique homomorphique.


Dans la réalité réelle, TFHE prend en charge plusieurs extensions de ce modèle (graphes arbitraires d'opérateurs de niveau inférieur, textes chiffrés de différents types, recherches de tables multi-sorties, multiples façons de regrouper les variables, etc.). Mais nous pouvons ignorer ces améliorations pour l'instant car la vision du réseau de recherche est déjà suffisamment puissante pour nous permettre de convertir un programme simple en un équivalent homomorphe et de l'exécuter. Nous pouvons donc nous concentrer sur la manière de procéder, au moins pour une première itération de la technologie.

Rendre les réseaux TFHE exécutables

En tant que tel, un réseau TFHE n’est qu’un modèle et n’est pas encore prêt pour une exécution correcte au sein d’une application homomorphe. Même si les modules, les matrices de poids et les tables de recherche sont entièrement spécifiés pour toutes ses couches, il lui manque encore un ingrédient crucial qui est la paramétrisation cryptographique.


Les paramètres de chiffrement dictent toutes les mesures possibles sur ce qui se passera au sein du réseau au moment de l'exécution : tailles concrètes du texte chiffré, dimensions tensorielles réelles des clés de commutation et d'amorçage internes aux PBS, diverses options algorithmiques au sein des opérateurs homomorphes de bas niveau, comment les précisions et le bruit du texte en clair les niveaux évoluent à travers le réseau et, finalement, les spécificités de la façon de chiffrer et de déchiffrer. Ils prédisent également l’utilisation de la mémoire et les performances.


Déterminer quel ensemble de paramètres optimise l'exécution d'un réseau TFHE peut s'avérer fastidieux, et en tout cas, extrêmement difficile - même pour les experts - à la manière d'un stylo et d'un papier comme aux débuts de FHE, car tout dépend de tout. . De plus, plusieurs ensembles optimaux peuvent coexister simultanément, nécessitant ainsi un arbitrage entre la taille de la clé publique, la latence du chemin critique ou le débit maximal. Heureusement, le problème de l’automatisation de cette tâche a été résolu au cours des dernières années et de puissants outils d’optimisation existent désormais pour déterminer rapidement la meilleure instanciation cryptographique d’un réseau TFHE donné.


Paramétrisation d'un réseau TFHE



Une fois instancié avec des paramètres cryptographiques, un réseau TFHE devient véritablement exécutable, ou plus exactement, peut devenir un véritable exécutable grâce à une passe de compilation appropriée.

Programmes TFHE

Un programme TFHE est une collection de réseaux TFHE paramétrés, collés ensemble par une « logique simple ».


La logique simple est faite de


  • instructions fonctionnant sur des variables en texte clair (c'est-à-dire des variables normales et non chiffrées),

  • branchement, soit inconditionnel, soit conditionné à des prédicats en clair,

  • lecture et écriture de la mémoire aux adresses en texte clair, arithmétique des pointeurs,

  • appels aux sous-programmes/fonctions.


Fondamentalement, la logique simple contient toute la logique de programmation prise en charge par le langage, à l'exclusion d'un seul cas : la modification des variables cryptées du programme, qui est la prérogative des parties TFHE du programme. La seule chose que la logique simple est autorisée à faire avec les textes chiffrés - et les clés publiques TFHE - est de les déplacer sans modification et de les transmettre aux parties TFHE, comme si celles-ci fonctionnaient dans leur propre coprocesseur ou conteneur séparé.


À toutes fins utiles, un programme répondant à cette définition est complet et prêt à devenir une application homomorphe à part entière, quel que soit le langage de programmation. La compilation personnalisée fournira ce mappage final, et l'objet résultant pourra ensuite être exécuté sur un environnement d'exécution compatible TFHE ou en tant qu'exécutable autonome et autonome.


Un programme TFHE


Un langage dédié peut être utilisé pour unifier la représentation des programmes TFHE - comme certains DSL, ou mieux, un dialecte MLIR - afin que la compilation puisse être effectuée avec le même compilateur back-end.


La nature précise du runtime (bibliothèque partagée/dynamique, VM ou autre) n'est ici qu'une modalité. L’une ou l’autre option mènera à une application homomorphe opérationnelle alimentée par TFHE qui pourra être déployée et exposée aux utilisateurs.


Revenons maintenant au jeu en cours pendant une minute.


Nous sommes face à un développeur qui ne connaît rien de TFHE ou de ce qui précède mais souhaite construire une application homomorphe. Supposons que nous ayons publié le compilateur décrit ci-dessus et le runtime compatible TFHE, le cas échéant.


Notre objectif est fixé, il nous suffit d'un programme TFHE pour arriver à un exécutable. Mais... comment diable allons-nous faire en sorte que le développeur auto-produise quelque chose d'aussi spécifique qu'un programme TFHE en premier lieu ?

La voie facile : publiez une bibliothèque FHE et laissez le développeur faire son travail

Voici la voie facile et gagnante : vous encapsulez toute la complexité dans une API FHE en boîte noire.

Programmes simples

Dans n'importe quel langage de programmation, un programme (simple) peut être considéré comme essentiellement une combinaison de 3 ingrédients :


  • la logique programmatique, composée d'instructions natives du langage et de constructions de cadrage,

  • des variables et une sélection de types de données que le programme leur affecte, choisis parmi tous les types de données possibles,

  • appelle une sélection de fonctions externes, sélectionnées parmi toutes les fonctions externes disponibles, que le programme utilise pour opérer sur ses variables.


Les types de données ont leur propre sous-langage, un mélange de types natifs et de constructions de typage pour étendre ces types natifs et les combiner en types structurés de niveau supérieur. Le système de types est censé fournir suffisamment d’expressivité pour couvrir pratiquement toutes les structures de données dont le programme peut avoir besoin. Les fonctions externes sont celles fournies par les bibliothèques, standard ou autres, mais elles peuvent également être invoquées implicitement par des instructions natives du langage (pensez aux opérateurs d'arithmétique modulaire ou de division).


Les programmes simples sont donc en fait « simples » :


Un programme simple, en substance


Écoutez-moi bien ici. Je ne dis pas que tous les concepts de programmation de haut niveau tels que le polymorphisme, les objets avec leurs membres et attributs, les classes et l'héritage hiérarchique, les modèles, les macros, les traits, les threads, la récursivité, le sucre syntaxique, les annotations et tous les autres concepts à coût nul imaginables les abstractions fournies par le langage sont nécessaires et simples à gérer pour le développeur, même si elles ont été inventées pour simplifier son travail.


Je dis simplement que pour notre propos, ce ne sont que des décorations inoffensives qui disparaissent au moment de la compilation, car une version réduite mais équivalente, de forme normale, du programme est déduite de la source. Cette version du programme, exprimée dans n'importe quelle représentation intermédiaire (IR), est constituée de blocs de base en ligne droite - des séquences d'instructions élémentaires dans cette IR - reliés par un graphe de flux de contrôle.


Or, c'est ce programme sous sa forme normale qui est "simple". Je veux dire, assez simple pour être augmenté de capacités homomorphes.

Jetez FHE dans l’image

Vous gagnez la partie en publiant une bibliothèque FHE native et en laissant le développeur déterminer la meilleure façon de l'utiliser.


La bibliothèque exposera de nouveaux types de données - ceux chiffrés, pour compléter les simples - et une collection de fonctions homomorphes qui imitent (plus ou moins) les fonctions simples que le développeur connaît, sauf qu'elles fonctionnent avec des types de données chiffrés. En bref, vous étendez le système de types et l'écosystème des bibliothèques et laissez l'intelligence du développeur déterminer comment utiliser ces extensions pour créer son application homomorphe.


Ceci n’est pas particulièrement lié au TFHE, n’importe quel programme FHE fera l’affaire. C'est sur cela que se concentrent les fournisseurs des différentes bibliothèques FHE : améliorer la convivialité de FHE en exposant des fonctions homomorphes de haut niveau qui semblent aussi proches que possible de l'expérience de codage simple. Toute la complexité cryptographique est résumée dans des boîtes noires vers lesquelles le programme appellera Oracle.


Cela peut bien sûr fonctionner bien pour le développeur. Eh bien... si vous remplissez votre part du marché en tant que fournisseur de bibliothèque, c'est le cas.


Ils trouveront un programme homomorphe qui ressemble maintenant à ceci.


Un programme homomorphe, en substance


Il existe désormais des variables simples et chiffrées coexistantes et le programme doit maintenir une séparation stricte entre ces 2 types d'objets. C'est parce qu'il existe cette règle d'or FHE qui dit que lorsque vous appliquez une fonction à un mélange d'arguments bruts et chiffrés, le résultat est nécessairement chiffré, par exemple fhe_add(E(x), y) renvoie E(x+y) et bientôt. Ainsi, des variables simples peuvent entrer dans certaines fonctions FHE mais ne peuvent jamais en sortir. Le chiffrement homomorphe « contamine » tout ce qu’il touche par le calcul.


Alors, voyez... comment créer une branche conditionnelle vers une variable chiffrée alors ?


Eh bien, vous ne pouvez pas. Mais ce n'est pas du tout un gros problème.

Le seul problème : le branchement conditionnel

Dans une application FHE, le branchement conditionnel ne peut agir que sur des booléens simples, pas sur des booléens chiffrés. Comment sauriez-vous où sauter en fonction d'un bit crypté ? Vous n'avez pas la clé privée de l'utilisateur pour déchiffrer ce bit.


Heureusement, FHE vous donne aussi des astuces simples pour y remédier.

Comment régulariser un if

Supposons que le développeur veuille initialement faire quelque chose comme


 if (x == 0) then y = 3 else z = 7


mais se rend compte qu'à ce stade, la variable x sera en fait cryptée. Comment adapter ce morceau de code ?


La première chose à faire est de retravailler l'instruction if simple pour obtenir un morceau de code équivalent en ligne droite qui utilise le multiplexage :


 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


Dans une deuxième passe, le développeur doit propager le fait que x est de type chiffré sur les lignes suivantes :


 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)))


On peut vérifier que cela est fonctionnellement équivalent à l'intention initiale du développeur, et c'est tout.


Si le langage de programmation permet de surcharger les opérateurs natifs, l'API FHE peut même rendre superflues les fonctions FHE explicites du deuxième extrait, de sorte que la première réécriture soit la seule chose que le développeur doit faire.


Pour donner une dose supplémentaire de sucre syntaxique au développeur, vous pouvez même exposer un opérateur ternaire surchargé a? b : c où n'importe quel argument peut être chiffré ou non. Le morceau de code devient encore plus simple :


 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


Cela se généralise facilement aux instructions if/switch arbitraires : chaque fois qu'il y a une condition cryptée à vérifier, le développeur n'a qu'à utiliser le multiplexage pour fusionner les multiples corps de l'instruction en un seul bloc de code linéaire équivalent.

Comment régulariser une boucle for/while

Désormais, les constructions de boucles impliquant des conditions chiffrées peuvent être régularisées dans le même esprit. Prenons par exemple


 for (i = 0; i < x; i++) do <body> // i is plain, x is encrypted


x doit être de type crypté. Tout d’abord, décomposez cela en une instruction for simple et une instruction if irrégulière :


 for (i = 0; i < known_max_value_of_x; i++) do if (i < x) then <body> // i is plain, x is encrypted


puis régularisez l'instruction if comme avant :


 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? _ : _


Notez que cela nécessite une valeur immédiate known_max_value_of_x . La valeur maximale prise en charge par le type chiffré de x peut être utilisée par défaut, mais dans de nombreux cas, le développeur connaît une bien meilleure limite supérieure sur x ce qui permet de réduire le nombre total de boucles au strict minimum.


En fin de compte, les transformations ci-dessus sont facilement généralisées en une méthode systématique de régularisation des flux de contrôle irréguliers, facile à assimiler et à ajouter aux codeurs.

Exemple : contrats intelligents confidentiels sur l'EVM

Le fhEVM de Zama est un framework open source à part entière pour le développement et le déploiement de contrats intelligents confidentiels sur la machine virtuelle Ethereum (EVM). Les contrats fhEVM sont de simples contrats Solidity qui sont construits à l'aide de chaînes d'outils Solidity traditionnelles. L'expérience de développement familière est augmentée par FHE par la bibliothèque TFHE.sol qui fournit des types de données cryptées et des remplacements FHE pour les fonctions standard.


Les types de données cryptées pris en charge sont actuellement


 ebool, euint4, euint8, euint16, euint32, euint64, eaddress


et les entiers signés cryptés seront bientôt également inclus. Les variables chiffrées sont créées à partir de textes chiffrés d'entrée bruts à l'aide de constructeurs dédiés :


 function mint(bytes calldata encryptedAmount) public onlyContractOwner { euint64 amount = TFHE.asEuint64(encryptedAmount); balances[contractOwner] = balances[contractOwner] + amount; totalSupply = totalSupply + amount; }


Les opérateurs natifs de Solidity +, -, *, &, |, ^, etc sont surchargés pour la commodité du développeur, et les opérateurs de comparaison fournis eq, ne, gt, lt, ge, le renvoient un ebool booléen chiffré. L'opérateur ternaire homomorphe _? _ : _ s'appelle select :


 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); }


Côté exécution, fhEVM fournit un EVM compatible TFHE dans lequel les fonctions homomorphes sont exposées sous forme de contrats précompilés, ce qui résulte de l'intégration de la bibliothèque open source TFHE-rs Rust.


Consultez le livre blanc fhEVM pour en savoir plus à ce sujet.

Que faut-il pour créer une API FHE avec TFHE ?

Rappelez-vous à quoi ressemblent les programmes TFHE exécutables, des réseaux TFHE paramétrés assemblés par une logique simple ? Eh bien, vous avez juste besoin d'un moyen de mapper la logique logicielle du développeur à cela.


La première exigence est de s’assurer que la logique du programme est « claire ». C'est exactement ce que nous avons appris au développeur à produire lui-même en régularisant ses instructions de flux de contrôle. Nous sommes donc vraiment bons là-dessus maintenant.


La deuxième exigence est que toutes les fonctions homomorphes appelées par le programme doivent être mappées sur des réseaux TFHE paramétrés préétablis. C’est plus complexe qu’il n’y paraît pour plusieurs raisons.

1. Vous devez créer des réseaux TFHE pour vos fonctions API

Pré-établir un réseau TFHE paramétré qui implémente une fonction donnée n'est pas nécessairement trivial.


Le simple fait de construire une addition homomorphe de 2 entiers non signés cryptés de 64 bits vous conduit à de nombreuses options techniques : comment représenter les entrées 64 bits en tant que vecteurs d'entiers modulaires ? Avec quel module (ou plusieurs modules) exactement ? Et puis, comment réaliser un circuit d'addition 64 bits avec des couches de recherches de table ?


Beaucoup de choix là-bas. Mais vous finirez par vous décider grâce à une bonne ingénierie et en menant de nombreuses expériences.

2. Vous devez normaliser les types de données cryptées

En supposant que vous ayez implémenté des réseaux TFHE pour toutes les fonctions de l'API, vous voulez vous assurer qu'ils peuvent être composés à volonté comme des blocs Lego.


Ceci n’est pas nécessairement garanti car la meilleure façon de représenter le même type de données chiffrées peut différer d’une fonction à l’autre. Vous devez donc adopter un format arithmétique commun pour représenter chaque type de données chiffrées, sans trop dégrader l'efficacité de vos réseaux TFHE.


Encore une fois, il existe de nombreuses options et vous devrez arbitrer entre elles.

3. Vous devez garantir une réelle composabilité

En supposant que tous les réseaux TFHE soient désormais entièrement compatibles dans leurs formats d'entrée/sortie, la composabilité n'est peut-être pas encore garantie.


En effet, les paramètres cryptographiques qui instancient un réseau TFHE peuvent ne pas être compatibles avec ceux utilisés pour instancier un autre. Plus particulièrement, le niveau de bruit de chiffrement dans les textes chiffrés d’entrée et de sortie doit être ajusté pour vérifier la composabilité réelle.


C'est similaire à l'impédance dans les circuits électroniques, il n'y a aucun moyen de connecter un circuit à un autre s'il y a une inadéquation d'impédance. Vous devez d'abord aligner leurs niveaux d'impédance, et c'est la même chose ici. Le moyen le plus simple d'y parvenir est d'utiliser des ensembles fixes de paramètres - voire peut-être un seul ensemble unique - qui sont réglés pour garantir l'alignement de toutes les fonctions de l'API. Par la suite, le format des clés publiques des utilisateurs sera fixé, ainsi que les paramètres utilisés dans le chiffrement et le déchiffrement des utilisateurs, quels que soient les codes développeurs.


Si vous trouvez un moyen de satisfaire ces 3 exigences lors de la conception et du paramétrage de vos réseaux TFHE, tout en obtenant de bonnes performances globales, alors félicitations ! Vous avez réussi.

Alors pourquoi les bibliothèques FHE ne seraient-elles pas suffisantes ?

Ils sont assez bons pour la programmation à usage général, en supposant encore une fois que l'API FHE est suffisamment complète pour fournir des remplacements homomorphes pour toutes les fonctions standard attendues par le développeur, avec une composabilité totale.


Mais ils ne sont peut-être pas suffisants pour les programmes spécialisés dans


  • des fonctions volumineuses et gourmandes en calcul comme dans l'apprentissage automatique,

  • fonctions personnalisées et non standard.


C'est alors qu'intervient la compilation homomorphe.

Le chemin difficile : publier un compilateur homomorphe

Le chemin difficile commence ici : pour gagner la partie en dehors de la programmation généraliste, vous allez désormais livrer un compilateur TFHE.


Le compilateur s'occupera de ce que le développeur ne sait pas comment faire lui-même. Il sera alimenté par l'apport du développeur - quel qu'il soit, votre appel - et devra compléter les parties manquantes pour arriver à un programme TFHE.


Examinons des exemples typiques d'applications non standard.

Inférence confidentielle avec des réseaux de neurones profonds

En transformant un simple réseau neuronal en un équivalent homomorphe, le développeur construira et déploiera un service d'inférence homomorphe dans lequel les entrées et sorties des utilisateurs sont cryptées de bout en bout.

Ce que le développeur fournit en entrée

Le développeur est censé connaître suffisamment bien l’apprentissage automatique pour produire un modèle quantifié entraîné, ou en posséder déjà un.


Les spécificités de la façon dont la quantification est effectuée sont vraiment importantes ici, car votre compilateur exigera que le modèle soit essentiellement un réseau TFHE - ou qu'il se prête facilement à un réseau via une simple réécriture. Les techniques open source disponibles sont connues pour prendre en charge cette forme de quantification, soit en post-quantifiant un modèle pré-entraîné, soit de préférence en effectuant une formation basée sur la quantification (QAT), qui atteint des niveaux de précision de pointe par rapport à à des modèles non quantifiés formés sur le même ensemble de données.


Essentiellement, les modules utilisés dans les couches du réseau TFHE sont des puissances variables de 2, de sorte que la précision des signaux d'activation est mesurée en bits. Les poids sont des entiers signés et les fonctions d'activation sont elles-mêmes quantifiées et instanciées sous forme de recherches dans des tables. Lorsque les activations totalisent une fonction de signe dur décalée avec un décalage appris, cette définition englobe les types de modèles tels que les BNN , les TNN et leurs généralisations multi-bits. Mais en principe, chacun est libre d’utiliser des tables de recherche arbitraires dans les fonctions d’activation, et celles-ci pourraient donc même être apprises lors de la formation pour atteindre de meilleures précisions.


Ce que le développeur ne sait pas faire, c'est convertir son réseau TFHE en un exécutable homomorphe. Le seul ingrédient manquant ici est donc simplement un paramétrage cryptographique de ce réseau, et c'est tout ce que votre compilateur devra faire avant de passer à l'étape de compilation back-end.

Que faut-il pour paramétrer un réseau TFHE ?

Rappelons que le paramétrage d'un réseau TFHE fournit une instanciation cryptographique exécutable. Il contrôle également toutes les mesures relatives à cette exécution, comme la taille totale des clés publiques de l'utilisateur et la durée totale d'exécution. La paramétrisation revêt donc ici une importance cruciale, car le développeur cherche à réduire la latence d’inférence à un minimum absolu.


Il existe beaucoup trop de degrés de liberté dans les paramètres cryptographiques d’un réseau TFHE pour les forcer tous. De plus, les métriques à optimiser dépendent de 2 composants totalement externes au réseau et dépendent des algorithmes que le runtime utilise pour effectuer les opérations TFHE de bas niveau :


  • Une collection de formules de bruit . Une formule de bruit relie les distributions d'entrée et de sortie du bruit de chiffrement aux extrémités d'un opérateur, en utilisant les paramètres de l'opérateur comme variables inconnues. Leur établissement nécessite une analyse scientifique humaine et une validation expérimentale.

  • Une collection de mesures de coûts . Les mesures de coûts prédisent l'efficacité multidimensionnelle (utilisation de la mémoire, temps d'exécution, etc.) d'un opérateur en fonction de ses paramètres. Ils sont généralement déduits de mesures de référence grâce à une analyse optimale.


Tout changement dans l'implémentation du runtime doit être reflété dans ces 2 modules, car ils sont à la fois fortement dépendants de l'algorithme et du matériel.


Les formules de bruit et le modèle de coût du moteur d'exécution, ainsi que le réseau TFHE donné, forment une instance spécifique de toute une classe de problèmes d'optimisation et cette instance est confiée à un optimiseur dédié. Nous parlons ici de programmation non linéaire en nombres entiers mixtes avec des objectifs multiples, donc trouver un front de Pareto d'ensembles optimaux de paramètres nécessite de résoudre cette classe non triviale de problèmes d'optimisation.


Générer le paramétrage optimal d'un réseau TFHE

Préparation technologique

De bonnes connaissances scientifiques et techniques ont permis de résoudre ce type de problèmes d'optimisation en quelques secondes, et les compilateurs TFHE tels que Concrete disposent déjà d'un optimiseur de paramètres TFHE efficace en tant que module interne.


Diverses améliorations peuvent rendre les optimiseurs TFHE encore plus rapides à l'avenir, mais même sans celles-ci, on peut considérer la paramétrisation des réseaux TFHE comme – à peu près – une affaire accomplie.

Analyse de données personnalisées à grande vitesse

Ce que le développeur fournit en entrée

Ces applications sont d’un tout autre genre. Le développeur détient la spécification mathématique d'une fonction personnalisée à implémenter, accompagnée d'une contrainte de précision. Prenons par exemple

x est un nombre réel compris entre 0 et 1, et utiliser une approximation G de F est acceptable tant que

Le développeur sait que l’implémentation G au dos d’une enveloppe en composant des fonctions API standard sera probablement bien trop sous-optimale.


Ce que votre compilateur fera, c'est fabriquer - à la volée - un nouveau réseau TFHE spécialement conçu pour satisfaire la spécification de G . Il le paramétrera ensuite et procédera à la compilation back-end pour produire l'application homomorphe.

Que faut-il pour synthétiser un réseau TFHE ?

Eh bien, c'est là que la route devient soudainement beaucoup plus cahoteuse.

À l’heure actuelle, il existe un manque de connaissances scientifiques sur la façon dont les réseaux TFHE, avec la définition exacte que j’ai donnée plus tôt, peuvent être synthétisés automatiquement. Même les recherches sur la synthèse de types de circuits adjacents qui s'appuient également sur l'arithmétique modulaire à valeurs entières sont, au mieux, rares. Sans parler de tout synthétiseur préexistant capable de réaliser cette tâche de A à Z.

Profiter de la synthèse booléenne

Une façon de résoudre le problème consiste à exploiter une fraction de la puissance de calcul des réseaux TFHE en les réduisant à des circuits booléens. Par exemple, un neurone TFHE peut être forcé à agir comme une porte booléenne ternaire.


par

  • fixer son nombre d'entrées à 3 et imposer x_1, x_2, x_3 à des valeurs 0/1,
  • fixer son module à m = 4 et ses poids à (w_1, w_2, w_3) = (2, -1, 1) ,
  • définir sa table de recherche sur [0, 1, 1, 0] .


En forçant brutalement tous les poids et tables possibles avec le même module, on peut alors constituer un dictionnaire de neurones TFHE pouvant servir à calculer des portes ternaires. La prochaine étape consiste à


  • utiliser un outil de synthèse booléenne pour générer un circuit booléen à partir de la spécification du développeur (de nombreux outils open source sont disponibles pour ce faire),
  • découper (alias partitionnement LUT à 3 voies) le circuit en portes ternaires appartenant au dictionnaire,
  • en remplaçant ces portes ternaires par des neurones équivalents,
  • remodeler le circuit en un réseau en couches de profondeur minimale.


Ce n’est qu’une stratégie illustrative parmi tant d’autres car les méthodes qui s’appuient sur des circuits booléens sont nombreuses. Vous pouvez également simplement considérer les portes binaires habituelles et les implémenter avec des neurones TFHE contraints. Cingulata du CEA et, plus tard, le transpilateur FHE de Google ont justement ouvert cette voie avec TFHE.

Préparation technologique

La synthèse booléenne utilise des techniques d'optimisation de circuit agressives et constitue globalement un problème technique résolu - ou à peu près, ce qui rend cette approche solide et pratique pour celui qui construit le compilateur.


Cependant, une fois qu’un réseau TFHE est généré de cette manière, sa largeur et sa profondeur peuvent devenir anormalement élevées, entraînant de mauvaises performances globales. Il existe donc une suspicion largement répandue selon laquelle en relâchant le conditionnement booléen - totalement artificiel - des neurones TFHE, on peut exploiter toute leur expressivité et obtenir des réseaux beaucoup plus petits et beaucoup moins profonds.


Mais encore une fois, des recherches supplémentaires sont nécessaires pour identifier clairement comment y parvenir. Il est possible que des approches totalement différentes, comme l’apprentissage du réseau TFHE avec une méthode de formation appropriée empruntée à l’apprentissage automatique, finissent par fournir des résultats supérieurs. Le temps nous le dira.

La quête d'un compilateur TFHE tout compris

En supposant que notre problème de synthèse soit résolu et génère des réseaux TFHE personnalisés efficaces, vous vous retrouverez prêt à rassembler toutes les pièces mobiles et à concevoir un compilateur qui fait tout le travail :


  1. Cela prendrait en entrée un programme simple, où les variables sensibles sont simplement annotées comme étant cryptées.

  2. Il accepterait des réseaux de neurones pré-entraînés ou d'autres modèles d'apprentissage automatique et les réinterpréterait en tant que réseaux TFHE.

  3. Il accepterait des maquettes de fonctions simplement constituées d'une spécification mathématique et effectuerait une synthèse à la volée pour générer des réseaux TFHE personnalisés.

  4. Cela transformerait tous les réseaux TFHE non paramétrés laissés en suspens dans l'unité de compilation en instances exécutables à l'aide d'un module d'optimisation chaque fois que nécessaire.

  5. Il effectuerait l’étape back-end de la compilation pour une variété d’environnements d’exécution TFHE cibles ou/et d’architectures matérielles.

  6. Il tirerait parti d’accélérateurs matériels spécifiques (via leur modèle de coût) pour permettre la synthèse et le paramétrage de réseaux TFHE plus rapides.


Bon sang, autant y ajouter un support pour une régularisation automatisée du flux de contrôle, afin que le développeur n'ait même plus à s'en soucier.


Cela offrirait aux créateurs d’applications FHE l’expérience de développement ultime.

Résumé

Dans le contexte de la programmation FHE à usage général, une bibliothèque TFHE peut offrir une modularité et une expérience de développement totalement autonome avec des chaînes d'outils préexistantes.


TFHE est pionnier dans les techniques de compilation spécifiques qui peuvent satisfaire les besoins des développeurs au-delà de ce stade, plus particulièrement pour l'inférence d'apprentissage automatique cryptée et, dans les années à venir, pour le traitement de données cryptées à grande vitesse et d'autres applications FHE personnalisées.


Dans l'ensemble, TFHE offre une voie technologique claire pour créer des chaînes d'outils FHE plus intégrées et adaptatives qui peuvent réussir dans le monde du développement de logiciels et donner naissance à une nouvelle vague de solutions axées sur la confidentialité que chacun peut créer et exécuter avec une facilité sans précédent.


En me concentrant uniquement sur les réseaux de recherche TFHE, je viens d'utiliser un modèle informatique parmi plusieurs que TFHE peut prendre en charge. À mesure que la recherche découvre progressivement davantage de ses capacités, il ne fait aucun doute que de nouvelles façons d’instrumenter le TFHE feront leur apparition.


Lesquels et quand, c'est une autre histoire. Mais derrière cette histoire se cachent toute une série d’autres questions passionnantes et potentiellement éclairantes concernant l’avenir de l’informatique confidentielle.


Crédits à Midjourney v6, avec quelques conseils de l'auteur