• Aucun résultat trouvé

2.3 Les arbres de Merkle réguliers

2.3.2 Évaluation des performances

2.3.2.1 Notations

Nous prendrons le cas de l’utilisation d’un AMR pour protéger la mémoire externe d’un système embarqué comme exemple pour évaluer les performances. Le processeur à l’intérieur du SoC communique avec la mémoire externe à travers, dans l’ordre :

• le réseau de communication interne du SoC (interne, donc protégé), • le contrôleur des arbres de Merkle (interne, donc protégé),

• le contrôleur de mémoire externe (interne, donc protégé) et

• le bus de communication entre le SoC et la mémoire externe (exposé).

Le contrôleur des arbres de Merkle se situe donc à l’intérieur du SoC entre le réseau interne et le contrôleur de mémoire externe.

Nous introduisons les notations suivantes :

• MAC est la fonction utilisée pour calculer le condensé cryptographique d’un groupe de nœuds (ou de données)

• l est la taille d’un nœud d’arbre exprimée en nombre de mots mémoire (par exemple l = 2 pour des nœuds de 64 bits et un bus mémoire de 32 bits)

• t0 est la latence initiale de lecture, en nombre de cycles d’horloge, de la mémoire ex-

terne, entre l’émission de la première requête de lecture sur le bus mémoire et le début de la réception la première donnée lue

• tlest le temps (additionnel à t0), en nombre de cycles d’horloge, nécessaire au transfert

d’un nœud de l’arbre

• Lire un groupe de a nœuds ou blocs de données prend donc trg = t0 + tl × a cycles

d’horloge, entre l’émission de la première requête de lecture sur le bus mémoire et la réception de la dernière donnée lue

Algorithme 3 Écriture-Vérifiée((i 6= s, j = a × j0+ j1), V )

1: (u, v0, v1) ← (i, j0, j1) . Indices temporaires

2: for k ∈ {0, ..., a − 1} do . Pour a nœuds frères

3: Xk← Read(u, a × v0+ k) . Lecture non vérifiée

4: end for

5: Dold← Digest(u + 1, v0, X0, ..., Xa−1) . Calcul du nœud père ancien

6: Xv1← V . La nouvelle valeur du nœud

7: Write((u, a × v0+ v1), Xv1) . Écriture non vérifiée de la nouvelle valeur du nœud

8: Dnew← Digest(u + 1, v0, X0, ..., Xa−1) . Calcul du nœud père nouveau 9: (u, v0, v1) ← (u + 1, bva0c, v0mod a) . Avancer au niveau supérieur

10: while u < s do . Pour les niveaux d’arbre

11: for k ∈ {0, ..., a − 1} do . Pour a nœuds frères

12: Xk← Read(u, a × v0+ k) . Lecture non vérifiée

13: end for

14: if Xv16= Doldthen . Si la comparaison échoue

15: error . Abandonner

16: end if

17: Dold← Digest(u + 1, v0, X0, ..., Xa−1) . Calcul du nœud père ancien

18: Xv1← Dnew . La nouvelle valeur du nœud

19: Write((u, a × v0+ v1), Xv1) . Écriture non vérifiée de la nouvelle valeur du nœud

20: Dnew← Digest(u + 1, v0, X0, ..., Xa−1) . Calcul du nœud père nouveau 21: (u, v0, v1) ← (u + 1, bva0c, v0mod a) . Avancer au niveau supérieur 22: end while

23: X0← Read(s, 0) . Lire à partir de la zone sécurisée

24: if X06= Doldthen . Si la comparaison échoue

25: error . Abandonner

26: end if

27: Write((s, 0), Dnew) . Écrire la nouvelle racine dans la zone sécurisée

émissions de la première et de la dernière requête d’écriture sur le bus mémoire5

• tMAC est le temps de calcul d’un MAC, en nombre de cycles d’horloge

• Nous considérons que la latence pour lire ou écrire la racine à l’intérieur de la puce a un impact négligeable sur les performances, en comparaison de la latence de la mé- moire externe qui est typiquement de l’ordre de centaines de cycles d’horloge dans les systèmes à moyenne ou haute performance

• Nous considérons que la latence mémoire est au minimum deux fois plus grande que le temps de calcul d’un MAC (t0 > 2 × tMAC)

Un arbre de Merkle équilibré, d’arité a et de profondeur s, protège exactement asblocs de données et il contient un seul nœud racine dans son dernier niveau. Pour un arbre de Merkle déséquilibré, le dernier niveau contient plus d’un nœud. La figure 2.13 présente un arbre de Merkle déséquilibré quaternaire qui protège un ensemble de données de 128 blocs (différent de as), ce qui donne deux nœuds dans le dernier niveau de l’arbre. Le nœud racine de ce

type d’arbre se calcule en regroupant un ensemble d’arbres déséquilibrés et en rassemblant les nœuds de leurs derniers niveaux dans un seul groupe, même si le nombre de nœuds est différent de l’arité a. Cette propriété sera utilisée par la suite et on suppose que le temps nécessaire pour calculer le MAC de ce dernier niveau est le même que celui pris pour un groupe complet de a nœuds.

Pour l’évaluation des performances, nous considérons un système avec les caractéristiques suivantes :

• Système à base d’ARM-v7 (32 bits) à 1 GHz, avec des lignes de cache de 256 bits • Une mémoire externe de 4 Gio, 32 bits, un bus mémoire 32 bits utilisant les deux fronts

de son horloge à 500 MHz (soit un débit pic de données de 4 Gio/s), une latence de

Dernier niveau différente de as Ensemble de données données Groupe de

FIGURE2.13 – Arbre de Merkle déséquilibré protégeant l’intégrité d’un ensemble de données

t0 = 100 cycles d’horloge du processeur (à 1 GHz)

• L’algorithme de chiffrement par bloc utilisé pour calculer le MAC est DES-X, les blocs de données ont une taille de 64 bits (l = 2)

• L’arité des arbres de Merkle est a = 4 (une ligne de cache)

• Un calcul de DES-X prend 4 cycles d’horloge du processeur, un calcul de MAC prend tM AC = 20 = 4 × (1 + a) (une adresse plus a blocs de données)

• Il existe quatre tailles de page mémoire différentes, toutes sont protégées avec des arbres déséquilibrés, avec une super racine

— 4 Kio, s = 4 + 1 = 5 — 64 Kio, s = 6 + 1 = 7 — 1 Mio, s = 8 + 1 = 9 — 16 Mio, s = 10 + 1 = 11

2.3.2.2 Initialisation

La fonction itérative, utilisée pour initialiser l’AMR (voir page 48), demande une lecture régulière du groupe de nœuds (lignes 3 à 5), un calcul de MAC (ligne 6) et une écriture régulière du nœud père (ligne 7).

Le temps de calcul du MAC tM AC est négligeable puisque ce calcul peut être effectué en

parallèle avec la lecture régulière du groupe de nœuds suivant et que le temps nécessaire pour ce calcul est plus petit que la latence mémoire (figure 2.14). Néanmoins, l’avant dernier calcul de MAC (le nœud (s − 1, a − 1)) ne peut pas être parallélisé avec la lecture du groupe suivant ((s − 1, 0), . . . , (s − 1, a − 1)) puisqu’il en fait partie (il doit être écrit avant que la lecture de ce groupe soit possible). De même, le calcul du MAC du nœud racine (s, 0) ne peut pas être parallélisé avec une autre opération.

Nous considérons aussi que les opérations d’écriture ne peuvent pas être effectuées en pa- rallèle avec les opérations de lectures régulières car elles utilisent les mêmes ressources maté- rielles. Par contre, l’écriture dans le nœud (s − 1, a − 2) peut être effectuée en parallèle avec le calcul de MAC du nœud (s − 1, a − 1). La figure 2.15 présente les différentes opérations réalisées pendant l’initialisation complète de l’arbre.

t

Calcul de MAC

tMAC

t0+ tl× a

Lecture d’un groupe de noeuds

Écriture d’un noeud

tl

FIGURE 2.14 – Comparaison entre le temps pris pour une lecture d’un groupe de nœuds, un

calcul de MAC et un écriture d’un nœud

Wn: Écrire un noeud Rg: Lire un groupe de noeuds MAC : Calcul de MAC

Rg Wn Rg Wn Rg Wn Rg Rg (0, 0) (0, a) (1, 0) (0, 2a) (1, 1) (0, 3a) (s − 2, 3a) MAC MAC MAC (s − 1, a − 2) MAC (s − 1, a − 1) (s − 1, a − 2) Wn Rg MAC Wn (s − 1, 0) (s, 0) (s, 0) (s − 1, a − 1) MAC (1, 0) (1, 1) (1, 2)

Le temps pris par l’initialisation du premier niveau (i = 1) est donc (il faut noter que la dernière écriture, celle du nœud (1, as−1− 1) a lieu après la lecture du premier bloc du niveau

suivant ((1, 0), . . . , (1, a − 1))) :

ti1 = (trg+ twn) × a s−1

Pour les niveaux intermédiaires i avec 1 ≤ i < s−1 (la même remarque que précédemment s’applique) :

tii = (trg+ twn) × a s−i

Pour l’avant dernier niveau (i = s − 1) (ici la dernière écriture ne peut pas être réordonnée avec la lecture suivante) :

tis−1 = (trg+ twn) × (a − 1) + trg+ tMAC

Enfin, pour l’initialisation du nœud racine :

tis = trg+ tMAC

Au total, pour l’initialisation de tous les niveaux,

ti = (trg+ twn) × s−1 X k=2 ak+ (trg+ twn) × (a − 1) + trg+ tMAC+ trg+ tMAC = (trg+ twn) × s−1 X k=2 ak+ (trg+ twn) × (a − 1) + 2 × (trg+ tMAC)

Avec les différentes tailles des pages utilisées dans notre exemple à base d’ARM, nous obtenons (en cycles d’horloge du processeur), les valeurs suivantes :

• 4 Kio : ti ≈ 0, 044 · 106 cycles

• 64 Kio : ti ≈ 0, 710 · 106 cycles

• 1 Mio : ti ≈ 11, 359 · 106 cycles

• 16 Mio : ti ≈ 181, 753 · 106cycles

Optimisation Nous pouvons imaginer un autre scénario d’initialisation d’arbre qui va per- mettre de supprimer totalement les lectures régulières en mémoire externe. Tout d’abord, les blocs de données étant initialement considérés comme non fiables, il est possible de les initiali- ser (par exemple à zéro) pendant l’initialisation de l’arbre, ce qui évite les lectures de données et les remplacent par des écritures, que l’on peut parfaitement paralléliser avec les calculs cryp- tographiques.

Pour les lectures des nœuds de l’arbre, ce scénario est basé sur une organisation intelligente du parcours d’arbre permettant de calculer les MAC partiellement et de reprendre le calcul dès que le bloc suivant est disponible. Une fois qu’un calcul de MAC est terminé, sa valeur est écrite dans la mémoire externe dans le nœud correspondant. Il est donc nécessaire de stocker

Algorithme 4 Init-Optimisée

1: j0← 0 . Numéro du nœud de niveau 1 en cours de traitement

2: while j06= as−1do . Pour les nœuds de niveau 1 (juste au dessus des données) 3: D1← Digest(1, j0, 0, 0, ..., 0) . Calcul du nœud (1, j0) avec données nulles

4: W rite((1, j0), D1) . Écriture non vérifiée du nœud

5: (u, v) ← (1, j0) . Coordonnées du nœud courant

6: done ← true . On vient de finir le calcul d’un nœud

7: while done do . Tant que l’on vient de terminer le calcul d’un nœud, avancer le calcul de son père 8: done ← f alse . On ré-initialise le marqueur de fin de calcul d’un nœud

9: (w, t) ← (u + 1, bvac) . Coordonnées du nœud père

10: if v mod a = 0 then . Le nœud fils est-il le premier de son bloc ?

11: Dw← DigestInit(w, t, Du) . Si oui, début du calcul de son père

12: else

13: Dw← DigestUpdate(Dw, Du) . Sinon, suite du calcul de son père 14: end if

15: if v mod a = a − 1 then . Le nœud fils est-il le dernier de son bloc ? 16: W rite((w, t), Dw) . Si oui, calcul du père est terminé donc écriture du père

17: done ← true . On vient de finir le calcul d’un nœud

18: end if

19: (u, v) ← (w, t) . On avance au niveau suivant

20: end while

21: j0← j0+ 1 . On avance au nœud suivant

22: end while

23: for j ∈ {0, ..., as− 1} do

24: W rite(0, j, 0) . Initialisation des blocs de données avec la valeur zéro 25: end for

en interne un bloc par MAC en cours de calcul. Pour un arbre équilibré à N niveaux, le coût est, au plus, de N blocs de stockage interne.

L’algorithme 4 présente l’opération optimisée d’initialisation. Les écritures régulières des blocs de données (ligne 24) peuvent être parallélisées avec les calculs de MAC (ligne 3) et les écritures régulières des nœuds (lignes 4 et 16) peuvent également être parallélisées avec les calculs partiels de MAC (lignes 11, 13).

On suppose que le calcul du MAC peut être effectué progressivement à l’aide des fonctions DigestInit et DigestUpdate (on suppose que le temps de calcul complet est toujours égal à tMAC) :

D0 = DigestInit(i, j, X0)

D1 = DigestUpdate(D0, X1)

D2 = DigestUpdate(D1, X2)

· · ·

Digest(i, j, X0, . . . , Xa−1) = DigestUpdate(Da−2, Xa−1)

Un tel découpage est facilement réalisable dans le cas de CBC-MAC.

Au total, le temps pris par cette opération optimisée d’initialisation pour tous les niveaux est : ti = tMAC× s−1 X k=0 ak

Avec les différentes tailles des pages utilisées dans notre exemple à base d’ARM, nous obtenons (en cycles d’horloge du processeur), les valeurs suivantes :

• 64 Kio : ti ≈ 0, 109 · 106 cycles

• 1 Mio : ti ≈ 1, 747 · 106 cycles

• 16 Mio : ti ≈ 27, 962 · 106cycles