Alors que le chiffrement homomorphe complet (FHE) a été initialement réalisé en 2009, il commence à recevoir plus d'attention maintenant que la puissance de calcul a rattrapé ses exigences de calcul ; cependant, l'objectif du cryptage homomorphe remonte à une décennie.
Une recherche rapide en trouvera plusieurs définitions. C'est le plus direct et le plus succinct :
Une forme de cryptage qui permet le calcul sur des textes chiffrés générant un résultat crypté qui, une fois décrypté, correspond au résultat des opérations comme si elles avaient été effectuées sur du texte en clair.
— Massimo Bertaccini ( Algorithmes de cryptographie )
Considérez cette déclaration un instant. Le cryptage homomorphe permet d'effectuer des opérations sur des valeurs cryptées de sorte que les résultats décryptés produisent le résultat des opérations comme si elles étaient effectuées sur les valeurs non cryptées d'origine. La première mention que j'ai trouvée est On Data Banks and Privacy Homomorphisms (1978) dans laquelle les auteurs tentent d'empêcher les programmeurs système d'accéder aux données financières sensibles stockées dans un système sur site à l'aide d'un service de partage de temps à distance.
Les données financières sensibles sont stockées dans la banque de données. Les services de temps partagé qui calculent les données n'implémentent pas une sécurité adéquate. Les programmeurs système ont accès aux données financières. Le papier précise :
Utilisez un homomorphisme de confidentialité spécial pour crypter ses données afin que l'ordinateur en temps partagé puisse fonctionner sur les données sans qu'il soit nécessaire de les décrypter au préalable.
En d'autres termes, je souhaite supprimer le traitement de mes données sans en céder l' accès .
Pour le dire autrement encore, function(A, B) = function (A , B
) où A and B
sont des valeurs chiffrées de A et B, respectivement. Cela permet à des tiers de calculer des données cryptées et de fournir les résultats sans avoir aucune connaissance de la valeur d'origine. Avec RSA, deux valeurs chiffrées avec la même clé peuvent être multipliées et le produit peut être déchiffré pour donner le produit des valeurs chiffrées.
Obtenez 5_6 à l'aide de RSA(5)_ RSA(6) :
RSA(5) = (5^17) mod 3233 = 3096
RSA(6) = (6^17) mod 3233 = 824
3096*824 = 254864
RSA^-1(254864) = 254864^2753 (mod 3233) = 30
5*6 = 30
Cependant, cela ne fonctionne pas pour l'addition. RSA est un schéma de chiffrement partiellement homomorphe (PHE) en ce sens qu'il ne prend en charge que la multiplication. La chronologie ci-dessous montre la progression des schémas (PHE) jusqu'au chiffrement quelque peu homomorphe (SHWE) qui permet des combinaisons limitées d'addition et de multiplication. Ces opérations pouvaient être répétées jusqu'à cinq fois avant d'aboutir à un résultat invalide. Puis vint le premier schéma FHE de Craig Gentry qui permettait des additions et des multiplications répétées sans perte de fidélité.
Depuis 2009, les programmes FHE et les bibliothèques se sont multipliés.
Une bibliothèque | Régimes | Langue | Licence |
---|---|---|---|
BFV, BGV, CKKS | Wrapper C++/C# | MIT | |
Ouvrir FHE | BFV, BGV, CKKS, DM, CGGI | C++ | Licence BSD à 2 clauses |
CKKS | C++ | Creative Commons 3.0 | |
TFHE (Tore) | Rouiller | BSD-3-Clause-Effacer | |
TFHE | Rouiller | BSD-3-Clause-Effacer | |
BFV, BGV, CKKS | Va | Apache 2.0 | |
N/A – Utilise SEAL, PALISADE, HLib | Python, Docker | Apache 2.0 |
Microsoft SEAL (Simple Encrypted Arithmetic Library) est développé en C++ 17 avec un wrapper C#. Sorti à l'origine en 2015, il est toujours en cours de développement. Le dépôt github comprend des exemples C# approfondis qui devraient être explorés si vous souhaitez approfondir.
Ces schémas sont pris en charge par SEAL et incluent des opérations homomorphes pour l'addition, la division, la soustraction et l'exponentiation, mais pas la division.
Brakerski-Gentry-Vaikuntanathan (BGV)/Fan-Vercauteren (BFV)
Nombres entiers (longs)
À utiliser si la précision est une exigence
Le calcul est plus lent que CKKS
Les valeurs chiffrées avec BGV peuvent être utilisées avec des valeurs chiffrées avec BFV
Les valeurs chiffrées avec BGV/BVF ne peuvent pas être utilisées avec des valeurs chiffrées avec CKKS
Revisiter les schémas de chiffrement homomorphes pour les champs finis
Ces exemples utilisent le package Microsoft.Research.SEALNet Nuget et le schéma CKKS. Les trackers de santé, comme FitBit et AppleHeath, nécessitent de renoncer à une certaine confidentialité. Ils collectent des informations personnelles telles que le nombre de pas et les coordonnées géographiques des mouvements du porteur, la fréquence des battements cardiaques et d'autres statistiques vitales. Avec un cryptage homomorphe, ces agrégateurs de mesures de santé peuvent toujours fonctionner sur nos données sans avoir accès aux données brutes.
Dans cet exemple simple, le client envoie des métriques d'exécution, y compris la distance et le temps. Le serveur agrège les données cryptées et calcule la vitesse moyenne.
Pour commencer, une clé publique est créée par le client et partagée avec le serveur. Il est utilisé pour chiffrer des données et également pour effectuer des opérations arithmétiques sur des données chiffrées. Le client et le serveur utilisent tous deux un Encryptor. Le client génère et conserve une clé privée utilisée pour le déchiffrement utilisé par le bien nommé Decryptor .
Une partie malveillante qui intercepte les données pourrait utiliser la clé publique sur les données cryptées et la renvoyer à l'appelant en usurpant le serveur. Le serveur doit signer le résultat et le client doit vérifier la signature pour s'assurer qu'elle est renvoyée par une partie de confiance.
using Microsoft.Research.SEAL; protected SEALContext _context; private KeyGenerator _keyGenerator; private Encryptor _encryptor; private Decryptor _decryptor; protected IFitnessTrackerApiClient _apiClient; . . . _context = SEALUtils.GetContext(_config.Value.PolyModulusDegree, this.SchemeType); _keyGenerator = new KeyGenerator(_context); _keyGenerator.CreatePublicKey(out PublicKey publicKey); _keyGenerator.CreateRelinKeys(out RelinKeys relinKeys); _encryptor = new Encryptor(_context, _publicKey); _decryptor = new Decryptor(_context, _keyGenerator.SecretKey); . . . PublicKeyModelCKKS keyModelCKKS = new( SEALUtils.KeyToBase64String(_publicKey), SEALUtils.KeyToBase64String(relinKeys)); await _apiClient.SendPublicKeyCKKSAsync(keyModelCKKS);
Les clés de relinéarisation sont transmises du client au serveur. Selon Microsoft SEAL, ils n'ont aucune signification sémantique autre que de réduire la taille du Ciphertext suite à une opération de multiplication.
Sur la ligne de commande, l'utilisateur entre le temps et la distance.
Chaque valeur est encodée en PlainText, puis chiffrée en Ciphertext et enfin encodée en base64 avant de l'envoyer au serveur.
using Microsoft.Research.SEAL; var plaintext = new Plaintext(); _encoder.Encode(value, _scale, plaintext); var ciphertext = new Ciphertext(); _encryptor.Encrypt(value, ciphertext); using (var ms = new MemoryStream()) { ciphertext.Save(ms); return Convert.ToBase64String(ms.ToArray()); }
Étant donné que les schémas de chiffrement homomorphes ne prennent pas en charge la division (CKKS ne fait pas exception), le client doit aider le serveur. La formule de la vitesse est :
distance/temps
Une autre façon de l'écrire est,
distance * (1/temps)
Ainsi, le client envoie :
RunItemCKKS metricsRequest = new( EncryptBase64(runItem.Distance), EncryptBase64(runItem.Time), EncryptBase64(1 / runItem.Time)); await _apiClient.AddNewRunningDistanceCKKSAsync(metricsRequest);
Le serveur exécute une fonction d'amorçage similaire en créant un SEALContext en utilisant uniquement la clé publique et les clés de relinéarisation, puis traite la requête :
var distance = SEALUtils.BuildCiphertextFromBase64String(request.Distance, _sealContext); var time = SEALUtils.BuildCiphertextFromBase64String(request.Time, _sealContext); var timeReciprocal = SEALUtils.BuildCiphertextFromBase64String(request.TimeReciprocal, _sealContext); Ciphertext speed = new(); _evaluator.Multiply(distance, timeReciprocal, speed); _evaluator.RelinearizeInplace(speed, _relinKeys); _runListCKKS.Add(new EncryptedRunInfoCKKS(distance, time, speed));
L'inverse du temps est utilisé pour calculer la vitesse de l'exécution soumise et la méthode RelinearizeInplace réduit la taille du texte chiffré résultant . La distance, le temps et la vitesse sont enregistrés dans une liste en mémoire.
La méthode GetMetrics agrège la liste et renvoie :
nombre total de courses
distance totale
temps total
vitesse moyenne
public SummaryItemCKKS GetMetrics() { int count = _runListCKKS.Count; var totalDistanceCKKS = SumEncryptedValues(_runListCKKS.Select(m => m.Distance)); var totalTimeCKKS = SumEncryptedValues(_runListCKKS.Select(m => m.Time)); var totalSpeed = SumEncryptedValues(_runListCKKS.Select(m => m.Speed)); . . . protected Ciphertext SumEncryptedValues(IEnumerable<Ciphertext> encryptedData) { . . . Ciphertext encTotal = new(); _evaluator.AddMany(encryptedData, encTotal); return encTotal; . . . }
Pour obtenir la vitesse moyenne, l'inverse du décompte est utilisé, donc plutôt que d'utiliser :
(somme de la vitesse)/nombre ou runs que nous utilisons :
(somme de la vitesse) * (1/nombre de courses)
Étant donné que le serveur conserve une trace du nombre de soumissions d'exécutions, le nombre total d'exécutions n'est pas une valeur chiffrée. Cette valeur non chiffrée peut être convertie en texte brut et utilisée dans une opération effectuée sur le texte chiffré. Bien qu'il s'agisse d'un exemple simple, les implications méritent d'être notées. Le serveur peut fournir des valeurs supplémentaires utilisées sur les données cryptées permettant aux fournisseurs tiers d'appliquer des données exclusives aux calculs par rapport aux données cryptées fournies par le client.
Dans l'exemple ci-dessous, le nombre total est converti en réciproque et ajouté à une List<double>. Les opérations CKKS attendent des valeurs de liste chiffrées, même si la liste ne contient qu'une seule valeur. Ainsi, le List<double> est encodé en un PlainText . MultiplyPlainInplace multiplie totalSpeed par (1/nombre d'exécutions), ce qui donne la vitesse moyenne. Pour économiser de l'espace, le résultat est appliqué au texte chiffré totalSpeed et les clés de relinéarisation réduisent la taille de la sortie.
Plaintext encodedCountReciprocal = new(); List<double> averagePaceList = new(); double runCountReciprocal = 1 / (double)count; averagePaceList.Add(runCountReciprocal); _encoder.Encode(averagePaceList, _scale, encodedCountReciprocal); _evaluator.MultiplyPlainInplace(totalSpeed, encodedCountReciprocal); _evaluator.RelinearizeInplace(totalSpeed, _relinKeys);
Les valeurs sont encodées en base64, renvoyées et déchiffrées sur le client. Le client prend l'étape supplémentaire de convertir le texte brut déchiffré en List<double> .
var payload = Convert.FromBase64String(encryptedDistanceText); using var ms = new MemoryStream(payload); var ciphertext = new Ciphertext(); ciphertext.Load(_context, ms); var decryptedText = new Plaintext(); _decryptor.Decrypt(cypherText, decryptedText); List<double> distanceList = new(); _encoder.Decode(decryptedText, distanceList);
L'envoi des valeurs suivantes donne les totaux et le rythme moyen calculés par un service qui totalise le total et la moyenne sans déchiffrer les données envoyées par le client.
Milles | Temps |
---|---|
2.5 | 0:35:32.643 |
2.2 | 0:32:48.826 |
2.8 | 0:34:52.036 |
Le serveur calcule les données et renvoie le nombre total de courses, la distance, le temps et le rythme sans décrypter aucune des données envoyées.
Microsoft SEAL est un projet Microsoft Research et n'est pas prêt pour une application de production. Toutes les classes implémentent IDisposable et utilisent des ressources non managées. Le code de production devrait se protéger contre une fuite de mémoire. La classe SEALContext ne respecte pas le principe de responsabilité unique. Il comprend des méthodes pour BGV/BVF et CKKS. De plus, CKKS prend en charge les opérations vectorielles, mais pas l'addition de vecteurs. Une seule List<double> peut être chiffrée en un seul CipherText mais il n'y a aucun moyen de sommer les valeurs dans un seul CipherText. Cela peut être fait avec la rotation vectorielle , mais c'est une solution sous-optimale.
Cet exemple de code Fitness Tracker est une introduction simple à un sujet complexe. Il existe un certain nombre de cas d'utilisation convaincants avec une logique plus complexe :
Le chiffrement homomorphe a été largement utilisé dans le milieu universitaire et dans les prototypes. Les applications pratiques ont été limitées par la puissance de calcul requise par les schémas de chiffrement homomorphe. L'accélération GPU et les processeurs ASIC continueront d'éroder cette barrière. Intel développe l' accélérateur matériel FHE , spécialement conçu pour les schémas de chiffrement homomorphe, en collaboration avec Microsoft et DARPA. Le NASDAQ finance également un projet de R&D utilisant le chiffrement homomorphe avec apprentissage automatique pour la détection des fraudes. La communauté FHE.org est une excellente ressource pour les développements les plus récents dans cet espace. Il a un serveur Discord actif.
Tous les exemples de code de cet article sont disponibles sur johniwasz/microsoft-seal-samples . Pour un exemple Microsoft SEAL plus approfondi, consultez ces exemples .NET .
Également publié ici.