Télécharger le fichier pdf d’un mémoire de fin d’études
Extraction et espionnage direct des composants
Un premier vecteur d’attaque possible est l’accès direct au programme par extraction ou lecture des composants mémoires. Ceci est particulièrement envisageable lorsque ces composants sont externes sur le circuit imprimé. Il est alors aisé d’accéder à ces composants, soit pour espionner les interfaces avec un analyseur logique, ou encore les extraire du support pour les analyser à l’aide de matériel spécifique.
Non seulement les mémoires persistantes (EEPROM, FLASH, . . .) sont concernées, mais également les mémoires dynamiques. La rémanence des données peut en effet être augmentée à plusieurs dizaines de minutes en refroidissant la mémoire, par exemple à l’aide d’azote liquide, on parle d’attaque par démarrage à froid (dites coldboot) [HSH+09].
Ces attaques concernent un grand nombre de systèmes. Cependant, la tendance aujourd’hui est d’intégrer de plus en plus les technologies, même différentes sur la même puce (System on Chip), ou du moins dans le même boîtier (System in Package).
Dans de tels systèmes, l’accès direct aux composants mémoire est très difficile, voire impossible.
Attaques logiques
Cependant, les entrées sorties du système resteront toujours accessibles à un utilisateur.
Ces ports peuvent présenter un point d’accès pour un attaquant au système.
L’attaque de Boileau [Boi06] illustre bien ce problème. L’auteur a réussi à partir d’un port externe de type FireWire à prendre le contrôle d’un composant de gestion de transfert mémoire (DMA). Dès lors, la mémoire principale du processeur peut être accédée en lecture et écriture, depuis ce port externe.
La logique de test et vérification du système (interface de déboggage, JTAG), lorsqu’elle n’est pas désactivée offre un autre point de vulnérabilité.
Fuites par partage d’éléments d’architecture
Les attaques présentées précédemment supposent un accès physique au système. On pourrait penser que ces attaques disparaissent pour un système inaccessible physiquement à l’utilisateur. Pourtant, le simple fait qu’un programme partage des ressources matérielles avec un autre programme potentiellement corrompu peut induire des fuites d’informations. On parle d’attaques par canaux auxiliaires logicielles. Les éléments d’architecture les plus dangereux à partager sont ceux qui influencent fortement les temps d’exécution, comme les prédicteurs de branchements, les mémoires caches ou encore les tables de traduction d’adresses virtuelles (TLB).
Aussi, on trouve un grand nombre d’attaques, les plus communes étant basées sur le partage de caches [Ber05]. Les auteurs arrivent en invalidant des lignes de cache et en analysant les latences d’accès à retrouver des informations sensibles d’un autre processus concurrent.
Contexte et motivations
Les mémoires DRAM sont également des éléments dangereux à partager comme l’illustre la vulnérabilité DRAMA [PGM+16]. La fuite ici est induite par le partage de lignes en mémoire DRAM. À partir de variations observées dans les temps accès, les auteurs parviennent à extraire des parties de la mémoire d’un autre processus, alors même que des mécanismes de protection mémoire (mémoire virtuelle, système d’exploitation) devraient les interdire.
Modèle de sécurité
Ainsi, il est clair que le stockage sur les mémoires externes ne peut pas être supposé fiable. De manière plus générale, dans un contexte de systèmes avec plusieurs applications concurrentes, le partage de n’importe quel élément d’architecture constitue potentiellement une fuite d’information. Nous essayons maintenant de formaliser le modèle de sécurité considéré dans cette thèse. La figure 1.1 représente l’architecture d’un système sur puce relativement simple. On y retrouve un processeur qui est connecté à divers périphériques par un bus. Le processeur possède ou non des mémoires caches. Il est relié à divers périphériques.
À l’extérieur de la puce sont présentes une mémoire FLASH pour permettre du stockage non volatile, ainsi qu’une mémoire dynamique (DRAM) pour le stockage des données à l’exécution.
L’objectif est de protéger la confidentialité d’un programme et de ses données.
Le programme et les données initiales sont stockés dans la mémoire externe. Dans ce contexte, nous supposons que la logique interne au processeur est une zone de confiance, dans laquelle les données sont supposées inaccessibles et non modifiables par un attaquant. Cette zone inclut les registres et les divers éléments de calculs comme les unités arithmétiques et logiques, éventuellement les unités de calcul flottant.
En revanche, cette zone de confiance s’arrête aux interfaces avec la mémoire, c’est-à-dire aux signaux de communication avec les caches, ou directement à un bus.
Ainsi, dans ce cadre, un attaquant peut accéder en lecture à toute donnée sortante de la zone de confiance (la logique d’exécution du processeur). Autrement dit, il peut :
— Lire à n’importe quel instant le code stocké en mémoire externe. Ceci modélise les menaces liées à l’observation directe ou indirecte des mémoires.
— Observer les données échangées sur le bus. Ce qui modélise l’espionnage par un composant corrompu, par exemple un autre processeur ou un DMA
— Accéder à n’importe quelle donnée présente dans les caches d’instructions ou de données s’ils existent. Cette capacité est donnée dans le but de modéliser les fuites de données directes ou indirectes par le cache.
Objectifs de la thèse et organisation du manuscrit
Questions abordées
Nous cherchons donc à assurer la confidentialité d’un programme sur un processeur en supposant l’existence d’une zone de confiance.
La première question abordée dans cette thèse est : comment peut-on maintenir la confidentialité jusqu’à cette zone de confiance ? Si cela est possible, nous chercherons évidemment à le faire le plus efficacement possible.
Les processeurs modernes utilisent une architecture dite de « Harvard», où les instructions et les données sont lues par des ports physiquement séparés. D’un coté, les accès à la mémoire d’instructions sont relativement séquentiels. De l’autre, les accès mémoire sont nettement moins structurés. Peut-on adresser plus efficacement la protection des instructions ou des données par des mécanismes dédiés à chaque domaine ?
Enfin, jusqu’à quelle limite peut-on maintenir les données confidentielles avant et pendant leur manipulation ? Peut-on, même les garder chiffrées dans cette zone de confiance ?
Structure du document
Nous commençons par dresser un état de l’art des solutions pour la confidentialité d’un programme dans le chapitre 2.
Nous présentons ensuite dans le chapitre 3 une méthode de chiffrement des instructions d’un programme permettant de déchiffrer à la frontière de la zone de confiance.
Le chapitre 4 présente une solution basée sur les algorithmes de chiffrement homomorphe pour maintenir les données chiffrées en mémoire et également durant les manipulations, c’est-à-dire à l’intérieur même de la zone de confiance. Le chapitre 5 présente la conception matérielle d’un coeur de calcul pour l’évaluation efficace des opérations homomorphes.
L’évaluation de ces propositions sur des composants logiques programmables est présentée dans le chapitre 6.
Enfin le chapitre 7 conclut ce manuscrit et synthétise les réponses apportées au cours de cette thèse.
Contributions
Les travaux présentés dans le chapitre 3 ont fait l’objet d’une présentation à la conférence DSD 2017 [HSG17]. Deux brevets ont été déposés sur cette thématique, un premier sur la méthode elle-même [PPSH17] et un second sur la combinaison de ce chiffrement avec des programmes auto modifiants (ce point n’est pas abordé dans ce manuscrit).
Une version préliminaire des travaux présentés dans les chapitres 4 et 5 a été présentée aux sessions doctorants de RESSI 2017 1. Un papier décrivant la solution complète est écrit et actuellement en cours de soumission. Un brevet a également été déposé sur ces travaux [HS17].
Méthodes pour la protection de la confidentialité d’un programme
Nous avons vu dans le chapitre précédent les différentes sources d’attaques qui peuvent compromettre la confidentialité d’un programme et de ses données. Nous effectuons dans ce chapitre un état de l’art des différentes solutions applicables à ce problème.
Nous commencerons par les techniques d’obfuscation, qui sera l’occasion de formaliser la problématique de la protection d’un programme. Puis, nous présenterons les différentes techniques utilisées dans les processeurs sécurisés. Dans un premier temps, les architectures qui introduisent du non-déterminisme d’exécution en vue de limiter les fuites d’informations par des canaux auxiliaires. Puis, par la suite, les architectures qui intègrent du chiffrement mémoire. Enfin, nous terminerons ce chapitre par une synthèse où seront identifiés les points sur lesquels les travaux de cette thèse amènent des améliorations.
Obfuscation, approche formelle au problème
L’obfuscation, est un anglicisme provenant du verbe obfuscate qui signifie obscurcir, embrouiller. Dans le monde de l’informatique, l’obfuscation réfère au procédé de transformation d’un programme visant à le rendre inintelligible.
L’obfuscation permet non seulement d’assurer la confidentialité des programmes, mais également de prévenir toute forme rétro-conception ou la réutilisation d’une partie du programme. Ceci permet de protéger la propriété intellectuelle d’un code ou de gérer des droits numériques (DRM). À des fins malveillantes, l’obfuscation est monnaie courante dans les malwares et est utilisée pour enfouir la partie dangereuse du programme, réduisant ainsi ses chances de détection. Enfin, à titre plus ludique, le concours International Obfuscated C Code Contest (http://www.ioccc.org/) récompense chaque année le code C le plus obscur.
Le programme de la figure 2.1 est très probablement écrit manuellement. On comprend que ce procédé peut-être assez long et ne permet donc pas d’obfusquer des programmes complexes. Heureusement, un nombre important de méthodes heuristiques permettent de brouiller automatiquement un programme. Par exemple, l’aplatissement du flot de contrôle, l’utilisation de prédicats opaques ou encore l’évaluation du programme dans plusieurs niveaux machines virtuelles imbriquées. Cependant, si des obfuscateurs complexes existent, il existe également des méthodes d’attaque et d’analyse de programmes obfusqués très performantes.
L’obfuscation au sens théorique
L’obfuscation bénéficie d’un traitement théorique rigoureux [BGI+01], à mi-chemin entre la cryptologie et la théorie de la complexité. Formellement, un obfuscateur O prend un programme P en entrée et produit un programme obfusqué O(P). Deux propriétés fondamentales sont attendues, quelle que soit le type d’obfuscation considérée:
— L’équivalence fonctionnelle : pour toutes les entrées, le programme obfusqué doit produire le même résultat que la version originale du programme.
— L’efficacité : la taille et le temps d’exécution du programme obfusqué doivent être comparables à l’original. Plus précisément, dans les deux cas un facteur au plus polynomial est toléré.
Un obfuscateur doit également offrir des propriétés de sécurité quant au programme obfusqué. La protection la plus forte est l’obfuscation en boîte noire dite Virtual Black Box (VBB). On impose alors que toute l’information obtenue sur la version obfusquée d’un programme soit simulable à partir d’un accès en boîte noire à ce programme.
La contrainte VBB est très forte et l’existence d’une telle obfuscation aurait des applications impressionnantes. Par exemple créer un schéma de cryptographie asymétrique à partir d’un schéma symétrique ou encore créer un schéma complètement homomorphe efficace. Il est malheureusement impossible de construire un obfuscateur générique au sens VBB pour toutes les fonctions existantes. Ce résultat très important du domaine a été démontré par Barak et. al dans [BGI+01] en construisant une famille de fonctions impossible à obfusquer.
Pourtant, ce résultat est loin d’avoir mis un terme à la recherche en obfuscation, en particulier un certain nombre de points restent ouverts :
— L’obfsucation au sens VBB est une contrainte très forte, qu’en est-il de modèles plus restreints ?
— La famille de fonctions impossible à obfusquer construite dans [BGI+01] ne constitue qu’un contre-exemple particulier. Le résultat d’impossibilité est tout de même applicable pour toute classe contenant TC0 (threshold circuits). Il s’agit de la famille des circuits de profondeur constante constitués d’un nombre polynomial de portes à vote majoritaire. Cependant, il reste un grand nombre de fonctions utiles pour lesquelles l’existence d’un obfuscateur n’est pas contredite par [BGI+01].
Obfuscation indistinguable
Une définition plus relâchée proposée dans [BGI+01] est l’Indistinguishability Obfuscation (iO). Pour deux circuits C1, C2 de même taille et qui calculent exactement la même fonction, la propriété d’iO impose qu’il soit impossible de distinguer O(C1) de O(C2).
Une construction générique d’un tel obfuscateur a été proposée dans [GGH+13], sous l’hypothèse d’existence des multilinear-maps. Cependant, la sécurité apportée par ce modèle plus restreint est moins intuitive que la propriété VBB.
Obfuscation de familles de fonctions particulières
Un premier cas intéressant d’obfuscation sont les fonctions à point (point functions) qui retournent faux pour toutes les entrées sauf une bien précise (voir figure 2.2). Ces fonctions sont utilisées dans la vérification de mot de passe.
Un domaine fortement lié à l’obfuscation est la whitebox cryptography. Il s’agit de protéger la clé d’une primitive cryptographique dans une implémentation logicielle complètement observable et modifiable (modèle VBB). L’objectif ici n’est pas de protéger la fonctionnalité, mais la clé. Le nom whitebox cryptography fait opposition à la cryptographie en boîte grise (greybox) qui réfère à la cryptographie sur les systèmes physiques où l’attaquant a accès à des informations partielles (e.g., canaux auxiliaires) et à la cryptographie théorique où dans les preuves, les primitives sont manipulées comme des boîtes noires (blackbox).
|
Table des matières
1. Contexte et motivations
1.1. Introduction
1.1.1. Processeurs et sécurité
1.1.2. Confidentialité d’un programme
1.2. Quelles menaces ?
1.2.1. Extraction et espionnage direct des composants
1.2.2. Attaques logiques
1.2.3. Fuites par partage d’éléments d’architecture
1.3. Modèle de sécurité
1.4. Objectifs de la thèse et organisation du manuscrit
1.4.1. Questions abordées
1.4.2. Structure du document
1.4.3. Contributions
2. Méthodes pour la protection de la confidentialité d’un programme
2.1. Obfuscation, approche formelle au problème
2.1.1. L’obfuscation au sens théorique
2.1.1.1. Obfuscation indistinguable
2.1.1.2. Obfuscation de familles de fonctions particulières
2.2. Techniques pour la protection des implémentations matérielles
2.2.1. Masquage
2.2.2. Processeurs non déterministes
2.3. Architectures de processeurs sécurisées
2.3.1. Les Oblivious RAMs
2.3.2. Architectures à chiffrement mémoire
2.3.3. Permutation aléatoire du jeu d’instructions
2.4. Comparaison et limites actuelles
2.5. Conclusion
3. Chiffrement d’un flot d’instructions
3.1. Introduction
3.2. Chiffrements par flots
3.2.1. Le chiffrement Trivium
3.2.2. Construction à partir d’un chiffrement par bloc
3.3. Chiffrement d’un programme
3.3.1. Chiffrement basé sur le flot de contrôle
3.3.2. Restauration des branchements entre blocs successifs
3.3.3. Blocs de base étendus
3.3.3.1. Fusion statique
3.3.3.2. Techniques de fusion avancées
3.3.4. Choix des vecteurs d’initialisation
3.3.4.1. Vecteur complètement aléatoire
3.3.4.2. Calculs à partir d’éléments d’adresse
3.3.5. Intégration dans une chaîne de compilation
3.3.5.1. Le projet LLVM
3.3.5.2. Avantages par rapport à un traitement sur le binaire
3.3.5.3. Pré-traitements
3.3.5.4. Chiffrement complet du programme
3.4. Support matériel
3.4.1. Activation et désactivation du chiffrement
3.4.2. Gestion des exceptions
3.4.3. Changement de contexte
3.5. Conclusion
4. Chemin de données homomorphe
4.1. Introduction
4.2. Chiffrement homomorphe
4.2.1. Définitions et terminologie
4.2.2. Construction d’un schéma complètement homomorphe
4.2.3. Évolution des schémas homomorphes
4.3. Méthode de bootstrapping matérielle
4.4. Le schéma Brakerski, Gentry, Vaikutanathan (BGV)
4.4.1. Préliminaires, notations
4.4.2. Chiffrement et déchiffrement
4.4.2.1. Génération de clé (Keygen)
4.4.2.2. Chiffrement (Encrypt)
4.4.2.3. Déchiffrement (Decrypt)
4.4.3. Opérations homomorphes
4.4.3.1. Addition Homomorphe
4.4.3.2. Multiplication Homomorphe
4.4.4. Détermination des paramètres
4.4.5. L’espace des messages, encodages
4.4.5.1. Encodage direct
4.4.5.2. Techniques de batching
4.5. Compilation pour l’évaluation homomorphe
4.5.1. Primitives usuelles
4.5.1.1. Propagation de bits
4.5.1.2. Multiplexeur
4.5.1.3. Suppression des branchements par if-conversion
4.5.1.4. Transformation des boucles
4.6. Évaluation homomorphe de l’algorithme AES
4.6.1. L’algorithme AES
4.6.2. Transformation de l’algorithme AES
4.7. Conclusion
5. Accélération matérielle pour le chiffrement homomorphe
5.1. Introduction
5.1.1. Les architectures existantes
5.1.2. Vue d’ensemble de l’architecture proposée
5.2. Réduction modulaire
5.2.1. Notations
5.2.2. Réduction par approximation du quotient (Barrett)
5.2.3. Réduction de Montgomery
5.2.4. Comparaison
5.3. Multiplication polynomiale
5.3.1. Multiplication dans Rq basée sur la transformée de Fourier
5.3.2. Calcul efficace de la NTT
5.3.2.1. Architecture
5.3.2.2. Organisation des accès mémoires
5.3.3. Performances, limites et améliorations possibles
5.4. Échantillonnage de gaussiennes discrètes
5.4.1. Choix de paramètres
5.4.2. Méthodes d’échantillonnage
5.4.2.1. Échantillonnage par rejet
5.4.2.2. Méthode d’inversion
5.4.3. Échantillonnage pour la procédure de chiffrement
5.5. Conclusion
6. Validation et Évaluation
6.1. Composant logique programmable (FPGA)
6.1.1. Architecture générale
6.1.2. La Famille de FPGAs Arria V
6.2. Processeur d’évaluation
6.3. Mécanisme de chiffrement des instructions
6.3.1. Validation et programmes d’évaluations
6.3.1.1. Vérification de code chiffré
6.3.1.2. Programmes d’évaluation
6.3.2. Empreinte matérielle
6.3.2.1. Matériel et paramètres d’évaluation
6.3.2.2. Synthèse du chiffrement par flots Trivium
6.3.2.3. Empreinte matérielle du chiffrement dans le processeur
6.3.3. Performances
6.4. Coprocesseur de calculs homomorphes
6.4.1. Stratégie de développement
6.4.2. Evaluation
6.4.2.1. Empreinte matérielle
6.4.2.2. Performances
6.5. Conclusion
7. Conclusion générale
A. Preuve de la proposition 5
Télécharger le rapport complet