Partager une clé secrète entre plusieurs personnes : la méthode de Shamir

Dans ce tutoriel, nous allons nous intéresser au partage de clé secrète et plus particulièrement, à l’algorithme de Shamir1. Ce que l’on veut dire par partage de clé secrète est que nous voulons chiffrer un message m et distribuer des parties à n personnes. Nous voulons ensuite que si k de ces n personnes (avec k < n) combinent leur partie, elles puissent récupérer le message m.

Afin d’aider à la compréhension, nous pouvons imaginer que nous sommes le patron d’une banque et nous voulons protéger le code (utilisons 5887 pour l’exemple) de notre coffre-fort. Nous avons 5 personnes de confiance à qui nous voulons distribuer une partie et si 3 d’entre d’elles combinent leur partie, elles devraient être en mesure de récupérer le code du coffre. Dans cet exemple que nous allons utiliser tout au long de ce tutoriel, m est le code du coffre (5887), n est le nombre de personnes de confiance (5) et k est le nombre de personnes suffisantes pour récupérer le code (3).

Dans le cadre de ce partage de clé, il est important que tout groupe de personnes composé de moins de k personnes ne puisse gagner aucune information sur notre code. Soit nous avons un nombre de clés suffisant et nous pouvons récupérer le code, soit ce n’est pas le cas et nous n’avons aucune information sur le code.

L'approche naïve et ses défaults

Avant même de commencer, regardons d’abord à quoi ressemblerait une approche simple et naïve à ce problème et pourquoi nous avons besoin de quelque chose de plus puissant. Après tout, si une approche simple est suffisante, à quoi bon se casser la tête à élaborer un algorithme complexe pour arriver au même résultat ?

L’approche la plus simple est de diviser notre clé en autant de personnes que nécessaire. Par exemple, si nous utilisons notre code 5887, on pourrait le diviser en quatre (5, 8, 8 et 7) et donner chaque chiffre à une personne différente. Ensuite, il suffira à ces personnes à se réunir et elle pourront récupérer le code initial. Cette approche a cependant plusieurs défauts:

  • Si l’une des quatre personnes perd son fragment de code ou si elle décède, elle est perdue à jamais et nous ne pourrons jamais reconstruire le code initial.
  • Recevoir un fragment nous donne une information importante sur le code initial. Aussi, combiner deux ou trois de ces fragments nous donne de plus en plus de connaissances quant à ce code. Avec trois fragments, nous pouvons même deviner le code initial en ayant une chance sur 10 d’avoir raison. Or, nous voulons vraiment éviter ce scénario ! Nous ne voulons donner des informations sur le code initial que si nous avons tous les fragments nécessaires (ici, quatre fragments).

Bien sûr, le premier défaut peut être plus ou moins amélioré en améliorant un peu notre stratégie mais le deuxième est beaucoup plus difficile à régler. C’est principalement pour cette raison que nous allons devoir faire appel à des algorithmes plus avancés.

Les fonctions et les notations

Fonctions

Avant de se lancer dans le vif du sujet, il nous faut faire un rapide rappel de mathématiques, en commençant par les fonctions.

Wikipedia propose la définition suivante:

En mathématiques, une fonction permet de définir un résultat (le plus souvent numérique) pour chaque valeur d’un ensemble appelé domaine.

Ici, nous allons nous intéresser au domaine des nombres réels R\mathbb R. Ainsi, par exemple, ceci est une fonction:

f(x)=x2f(x) = x^2

Pour chaque valeur xx de notre domaine R\mathbb R, cette fonction définit un résultat f(x)=x2f(x) = x^2. Par exemple, pour la valeur x=2x = 2, le résultat de notre fonction est 4. Et ceci peut être calculé pour tout xRx \in \mathbb R.

x f(x)
0 0
1 1
2 4
3 9
3,5 12.15
4 16
10 100

Nous pouvons aussi représenter cette fonction sur un graphe à deux dimensions où l’axe horizontal représentera les valeurs que prend xx tandis que l’axe vertical représentera les valeurs que prend f(x)f(x). Pour noter les points sur un tel graphe, nous utilisons la notation (x,y)(x, y)xx est la valeur de l’abscisse (la distance horizontale du point à l’origine) et yy est la valeur de l’ordonnée (la distance verticale du point à l’origine).

Graphe x^2
Graphe x^2

Et nous pouvons bien voir que la courbe tracée passe par les points calculés dans le tableau défini plus haut.

Notations

Avant de continuer, il nous faut aussi rapidement introduire deux notations qui seront utilisées au long de ce tutoriel : \sum et \prod. Il s’agit respectivement des signes de somme et de produit. Par exemple, plutôt que d’écrire f(X)=X+X2+X3...+X100f(X) = X + X^2 + X^3 ... + X^{100} nous allons plutôt écrire

f(X)=i=1100Xif(X) = \sum_{i=1}^{100} X^i

Et de même pour le produit où nous allons préférer

i=1100Xi\prod_{i=1}^{100} X^i

à X×X2×X3×...×X100X \times X^2 \times X^3 \times ... \times X^{100}.

Pour les programmeurs d’entre vous, vous pouvez voir ces opérations comme des boucles effectuant des additions ou des multiplications.

Un autre symbole que nous utiliserons dans ce tutoriel est \forall qui signifie "pour tout". Ainsi, si nous écrivons f(x)=0,xRf(x) = 0, \forall x \in \mathbb R, il faut le lire comme "la fonction f(x)f(x) vaut 00 pour tout xx dans R\mathbb R".

Fonction polynomiale

Une famille particulière de fonctions que nous allons utiliser dans ce tutoriel est celle des fonctions polynomiales. Voyons ce que Wikipedia peut nous dire à leur propos1:

En mathématiques, une fonction polynomiale (parfois appelée fonction polynôme) est une fonction obtenue en évaluant un polynôme.

Je vous l’accorde, ça ne nous avance pas beaucoup… Pour être plus concret, une fonction polynomiale est une fonction qui peut s’écrire sous la forme

f(X)=anXn+an1Xn1+...+a1X+a0=i=0naiXif(X) = a_n X^n + a_{n-1} X^{n-1} + ... + a_1 X + a_0 = \sum_{i=0}^n a_i X^i

où les valeurs a0,a1,...,an1,ana_0, a_1, ..., a_{n-1}, a_n sont appeléss coefficients et sont des valeurs réelles ou complexes.

Pour vous aider à comprendre ce qu’est une fonction polynomiale, voici quelques exemples:

f(X)=X2f(X) = X^2

Et nous pouvons vérifier qu’il s’agit bien d’une fonction polynomiale car X2=a2X2+a1X+a0X^2 = a_2 X^2 + a_1 X + a_0a2=1,a1=0,a0=0a_2 = 1, a_1 = 0, a_0 = 0.

f(X)=X5+10X42X3+58f(X) = X^5 + 10 X^4 - 2 X^3 + 58

Et nous pouvons vérifier qu’il s’agit bien d’une fonction polynomiale car X5+10X42X3+58=a5X5+a4X4+...+a1X+a0X^5 + 10 X^4 - 2 X^3 + 58 = a_5 X^5 + a_4 X^4 + ... + a_1 X + a_0a5=1,a4=10,a3=2,a2=0,a1=0,a0=58a_5 = 1, a_4 = 10, a_3 = -2, a_2 = 0, a_1 = 0, a_0 = 58.

Par contre, le fonction suivante n’est pas polynomiale.

f(X)=eX+X2f(X) = e^X + X^2

En effet, eXe^X ne peut pas s’écrire sous la forme anXna_n X^n.

Interpolation de Lagrange

Maintenant que nous savons ce qu’est une fonction et que nous savons trouver tous les points par lesquels elle passe, nous allons nous intéresser à l’opération inverse : comment trouver une fonction polynomiale passant par certains nombres distincts donnés ?

Pour trouver ce polynôme, nous allons appliquer une méthode appelée l’interpolation de Lagrange. Celle-ci se base sur le fait que si nous partons de n+1 points (x0,y0),(x1,y1),...,(xn,yn)(x_0, y_0), (x_1, y_1), ..., (x_n, y_n), il est toujours possible de trouver une et une seule fonction polynomiale de degré maximal n qui passe par tous ces points.

Par exemple, si nous partons des points (0,5),(1,3),(2,3)(0, 5), (-1, 3), (-2, 3) et appliquons l’interpolation de Lagrange, nous serons en mesure de retrouver le polynôme de degré 2 passant par tous ces points (en l’occurrence, f(X)=X2+3X+5f(X) = X^2 + 3X + 5).

Nous allons d’abord donner l’approche générique en termes mathématiques puis l’illustrer avec un exemple. Si vous n’arrivez pas à suivre la partie stricte, jetez un œil à l’exemple, il devrait vous aider à comprendre comment cette méthode fonctionne.

Pour trouver ce polynôme, nous allons d’abord calculer les polynômes de Lagrange qui s’écrivent de la façon suivante:

li(X)=Xx0xix0×...×Xxi1xixi+1×Xxi+1xixi+1×...×Xxnxixn=j=0,jinXxjxixjl_i(X) = \frac{X - x_0}{x_i - x_0} \times ... \times \frac{X - x_{i-1}}{x_i - x_{i+1}} \times \frac{X - x_{i+1}}{x_i - x_{i+1}} \times ... \times \frac{X - x_n}{x_i - x_n} = \prod^{n}_{j=0, j\neq i} \frac{X - x_j}{x_i - x_j}

Pour chacun de ces polynômes, il faut noter les choses suivantes:

  1. Pour chaque xjx_jjij \neq i, li(xj)=0l_i(x_j) = 0. En effet, pour ces valeurs, le numérateur s’annulera pour l’une des fractions présentes dans le produit.
  2. Pour xix_i, li(xi)=1l_i(x_i) = 1. Ceci vient du fait que dans ce cas, le numérateur et le dénominateur sont égaux.

Maintenant, nous pouvons utiliser ces polynômes de Lagrange et les combiner afin de trouver le polynôme de degré maximal n passant par tous les n+1 points:

L(X)=i=0nyili(X)L(X) = \sum_{i=0}^{n} y_i l_i(X)

En utilisant les propriétés des polynômes de Lagrange présentées ci-dessus, nous pouvons aisément conclure que L(xi)=yi,i0,...,nL(x_i) = y_i , \forall i \in {0, ..., n}.

Prenons un exemple pour illustrer cette méthode. Nous allons utiliser les 3 points suivants: x0=(1,2)x_0 = (1, 2), x1=(5,10)x_1 = (5, 10) et x2=(4,6)x_2 = (4, 6). En utilisant ces points, nous pouvons calculer les polynômes de Lagrange suivants:

{l0(X)=X515×X414=112(X29X+20)l1(X)=X151×X454=14(X25X+4)l2(X)=X141×X545=13(X26X+5)\left\{\begin{aligned} l_0(X) = \frac{X - 5}{1 - 5}\times \frac{X - 4}{1 - 4} &= \frac{1}{12}(X^2 - 9X + 20) \\ l_1(X) = \frac{X - 1}{5 - 1}\times \frac{X - 4}{5 - 4} &= \frac{1}{4}(X^2 - 5X + 4) \\ l_2(X) = \frac{X - 1}{4 - 1}\times \frac{X - 5}{4 - 5} &= \frac{-1}{3}(X^2 - 6X + 5) \end{aligned}\right.

Et nous pouvons donc trouver le polynôme passant par les points donnés:

L(X)=y0l0(X)+y1l1(X)+y2l2(X)=212(X29X+20)+104(X25X+4)63(X26X+5)=(212+10463)X2(1812+504363)X+(4012+404303)=23X22X+103\begin{aligned} L(X) &= y_0 l_0(X) + y_1 l_1(X) + y_2 l_2(X) \\ &= \frac{2}{12}(X^2 - 9X + 20) + \frac{10}{4}(X^2 - 5X + 4) - \frac{6}{3}(X^2 - 6X + 5) \\ &= (\frac{2}{12} + \frac{10}{4} - \frac{6}{3}) X^2 - (\frac{18}{12} + \frac{50}{4} - \frac{36}{3}) X + (\frac{40}{12} + \frac{40}{4} - \frac{30}{3}) \\ &= \frac{2}{3} X^2 - 2X + \frac{10}{3} \end{aligned}

Nous pouvons nous convaincre que ce résultat est correct en vérifiant que Li(xi)=yiL_i(x_i) = y_i:

{L(x0)=L(1)=232+103=2L(x1)=L(5)=50310+103=10L(x2)=L(4)=3238+103=6\left\{\begin{aligned} L(x_0) &= L(1) = \frac{2}{3} - 2 + \frac{10}{3} = 2 \\ L(x_1) &= L(5) = \frac{50}{3} - 10 + \frac{10}{3} = 10 \\ L(x_2) &= L(4) = \frac{32}{3} - 8 + \frac{10}{3} = 6 \end{aligned}\right.

La dernière chose qu’il nous reste à prouver est que le polynôme trouvé par cette méthode est bien unique.

Pour ce faire, nous allons imaginer l’existence d’un autre polynôme G(X)G(X) passant également par les points (x0,y0),(x1,y1),...,(xn,yn)(x_0, y_0), (x_1, y_1), ..., (x_n, y_n). Pour chacun de ces points, nous avons donc L(xi)=yiL(x_i) = y_i et G(x1)=yiG(x_1) = y_i (où i0,1,...,n1,ni \in {0, 1, ..., n-1, n}). Nous pouvons aussi définir une fonction D(X)=L(X)G(X)D(X) = L(X) - G(X) et réaliser que

D(xi)=L(xi)G(xi)=yiyi=0i{0,1,...,n1,n}\begin{aligned} D(x_i) &= L(x_i) - G(x_i) = y_i - y_i = 0 \\ \forall i &\in \{0, 1, ..., n-1, n\} \end{aligned}

Cela signifie que D(X)D(X) est un polynôme de degré nn valant 00 pour n+1n + 1 valeurs de X1. Or il n’existe qu’un seul polynôme pour lequel ceci peut être vrai: le polynôme D(X)=0D(X) = 0. Or, si D(X)=0D(X) = 0, cela signifie que L(X)=G(X)L(X) = G(X). Ce qui prouve que le polynôme trouvé via la méthode d’interpolation lagrangienne est le seul polynôme de degré maximal nn passant par les n+1n+1 points donnés.


  1. On dit que D(X)D(X) a n+1n +1 racines

Génération des fragments

Tout d’abord, nous allons nous attarder à la génération des fragments. Pour rappel, les fragments sont les informations qui seront données aux personnes de confiance et qui pourront être combinées pour récupérer le code initial.

Pour ce faire, nous allons construire une fonction polynomiale de degré k1k-1. Pour rappel, k est le nombre de clés suffisant à fournir lors de l’étape de reconstruction du code.

Cette étape est relativement simple, notre fonction étant polynomiale, elle s’écrit sous la forme

P(X)=i=0k1aiXi\begin{aligned} P(X) &= \sum_{i = 0}^{k-1} a_i X^i \end{aligned}

Nous pouvons choisir les termes ak1,ak2,...,a1a_{k-1}, a_{k-2}, ..., a_1 de façon aléatoire dans R\mathbb R1 et nous utilisons le code m pour a0a_0. Ainsi, cela nous donne P(X)=m+i=1k1aiXiP(X) = m + \sum_{i = 1}^{k-1} a_i X^i. Avec cette fonction, nous pouvons générer un nombre n de fragments au format (xi,yj)=(xj,P(xj))(x_i, y_j) = (x_j, P(x_j)) où l’on choisira xjx_j incrémentalement en partant de 1.

Tout ceci peut paraitre très abstrait, utilisons donc notre exemple de base où m = 5887, k = 3 et n = 5. Nous devons donc générer un polynôme de degré 2 sous la forme P(X)=a2X2+a1X+5887P(X) = a_2 X^2 + a_1 X + 5887. Nous pouvons choisir a1a_1 et a2a_2 aléatoirement. Utilisons a1=1689a_1 = 1689 et a2=250a_2 = 250. Cela nous donne

P(X)=250X2+1689X+5887P(X) = 250 X^2 + 1689 X + 5887

Maintenant que nous avons notre fonction polynomiale, il ne nous reste plus qu’à générer nos 5 fragments:

P(1)=7826key1=(1,7826)P(2)=10265key2=(2,10265)P(3)=13204key3=(3,13204)P(4)=16643key4=(4,16643)P(5)=20582key5=(5,20582)\begin{aligned} P(1) = 7826 &\Rightarrow key_1 = (1, 7826) \\ P(2) = 10265 &\Rightarrow key_2 = (2, 10265) \\ P(3) = 13204 &\Rightarrow key_3 = (3, 13204) \\ P(4) = 16643 &\Rightarrow key_4 = (4, 16643) \\ P(5) = 20582 &\Rightarrow key_5 = (5, 20582) \end{aligned}

Ces fragments peuvent maintenant être distribués aux 5 personnes de confiance sans qu’aucun de ces fragments individuellement ne dévoile quoi que ce soit quant à la valeur du code secret.


  1. Si vous vous demandez comment choisir un nombre aléatoirement dans R\mathbb R et si c’est même possible, bravo à vous, vous avez détecté un souci dans cette méthode. Nous y reviendrons plus loin dans ce tutoriel.

Récupération du code à partir des fragments

Très bien, nous avons 5 fragments, comment pouvons-nous maintenant récupérer le code secret en utilisant 3 de ces fragments ? C’est ici que l’interpolation lagrangienne va rentrer en jeu.

Notre code de déchiffrement va recevoir un nombre k de fragments (x1,y1),(x2,y2),...,(xk,yk)(x_1, y_1), (x_2, y_2), ..., (x_k, y_k). À partir de ces fragments, il est possible de retrouver l’unique fonction polynomiale de degré maximal k1k-1 passant par ces k points comme présenté plus tôt dans ce tutoriel. Nous pouvons calculer les polynômes de Lagrange li(X)l_i(X) puis les utiliser pour trouver la fonction P(X) passant par ces points. Cette fonction sera de la forme

P(X)=ak1Xk1+ak2Xk2+...+a1X+a0\begin{aligned} P(X) &= a_{k-1} X^{k-1} + a_{k-2} X^{k-2} + ... + a_1 X + a_0 \end{aligned}

Or, lors de l’étape de génération des fragments, on a décidé que notre code secret mm était utilisé pour la valeur a0a_0. TADA ! Notre code est récupéré et on peut le retourner à nos 3 personnes de confiance.

Une fois encore, réutilisons notre exemple pour mieux visualiser ceci. Nous allons utiliser les fragments (1,7826),(2,10265),(4,16643)(1,7826), (2,10265), (4, 16643) (n’importe quelle autre combinaison de 3 fragments fonctionnerait aussi). Calculons maintenant nos polynômes de Lagrange:

{l0(X)=X212X414=13(X26X+8)l1(X)=X121X424=12(X25X+4)l2(X)=X141X242=16(X23X+2)\left\{\begin{aligned} l_0(X) = \frac{X - 2}{1 - 2} \frac{X - 4}{1 - 4} &= \frac{1}{3}(X^2 - 6X + 8) \\ l_1(X) = \frac{X - 1}{2 - 1} \frac{X - 4}{2 - 4} &= \frac{-1}{2}(X^2 - 5X + 4) \\ l_2(X) = \frac{X - 1}{4 - 1} \frac{X - 2}{4 - 2} &= \frac{1}{6}(X^2 - 3X + 2) \end{aligned}\right.

Et avec ces polynômes, on peut calculer notre fonction P:

P(X)=y0l0(X)+y1l1(X)+y2l2(X)=78263(X26X+8)102652(X25X+4)+166436(X23X+2)=(78263102652+166436)X2(15652513252+166432)X+(62608320530+166433)=250X2+1689X+5887\begin{aligned} P(X) &= y_0 l_0(X) + y_1 l_1(X) + y_2 l_2(X) \\ &= \frac{7826}{3}(X^2 - 6X + 8) - \frac{10265}{2}(X^2 - 5X + 4) + \frac{16643}{6}(X^2 - 3X + 2) \\ &= (\frac{7826}{3} - \frac{10265}{2} + \frac{16643}{6}) X^2 - (15652 - \frac{51325}{2} + \frac{16643}{2}) X + (\frac{62608}{3} - 20530 + \frac{16643}{3}) \\ &= 250 X^2 + 1689 X + 5887 \end{aligned}

Et notre code secret est bien 5887 !

Place à l'arithmétique modulaire

Cette partie n’est pas nécessaire pour comprendre le cœur de la méthode de Shamir mais elle est indispensable pour que cette méthode soit strictement correcte et que sa sécurité soit garantie. Elle implique cela dit des calculs un peu plus compliqués que ce que nous avons vu jusqu’ici. Si vous n’arrivez pas à suivre cette section, ne vous en faites pas, elle ne bouleverse pas l’idée de base que nous avons utilisée plus haut.

Il est maintenant temps pour nous de revenir sur une note mentionnée plus tôt dans ce tutoriel. Nous avons énoncé que les coefficients a1,a2,...,an1,ana_1, a_2, ..., a_{n-1}, a_n étaient choisis aléatoirement dans R\mathbb R. Comment faire cela en pratique ? La réponse est très simple : c’est impossible. R\mathbb R est un ensemble infini, il nous est donc impossible de choisir des élements de façon uniformément aléatoire. Pour y remédier, nous allons passer à une autre forme d’arithmétique : l’arithmétique modulaire.

Nous allons donc plus choisir nos coefficients dans R\mathbb R mais plutôt dans Zp\mathbb Z_p, c’est à dire l’ensemble des nombres entiers modulo p.

C’est quoi un modulo ?

Modulo, c’est l’opération qui nous donne le reste d’une division. Par exemple, 17mod5=217 \mod 5 = 2 car 17=35+217 = 3 * 5 + 2 (22 est le reste de la division de 17 par 5). Faire de l’arithmétique dans Zp\mathbb Z_p plutôt que dans Z\mathbb Z ou R\mathbb R signifie qu’on se limite aux nombres entre 0 et p-1.

Pour continuer à utiliser notre exemple, regardons quelques opérations dans Z5\mathbb Z_5:

(17+11)mod5=28mod5=3(17 + 11) \mod5 = 28 \mod5 = 3
(3×7)mod5=21mod5=1(3 \times 7) \mod5 = 21 \mod5 = 1

Notre ensemble est donc maintenant fini et il est possible de choisir des nombres aléatoirement et de façon uniforme dans cette ensemble.

Il ne nous reste plus qu’à préciser un détail : pour que notre interpolation lagrangienne fonctionne correctement en arithmétique modulaire, il nous faudra choisir un nombre pp premier. Nous avons déjà abordé pas mal de concepts mathématiques et prouver cette propriété serait assez compliqué. Donc pour ne pas trop dévier du sujet initial, cette affirmation ne sera pas démontrée ici mais vous pouvez essayer le démontrer vous-mêmes.

Qu’est-ce que ça change pour notre algorithme ?

Pour voir comment ceci affecte notre algorithme, prenons un nombre premier p>mp > m, par exemple 6301. Nous pouvons donc recalculer nos clés en utilisant la formule suivante:

P(X)=250X2+1689X+5887mod6301P(X) = 250 X^2 + 1689 X + 5887 \mod 6301

Maintenant que nous avons notre fonction polynomiale, il ne nous reste plus qu’à générer nos 5 clés:

P(1)=7826mod6301=1525key1=(1,1525)P(2)=10265mod6301=3964key2=(2,3964)P(3)=13204mod6301=602key3=(3,602)P(4)=16643mod6301=4041key4=(4,4041)P(5)=20582mod6301=1679key5=(5,1679)\begin{aligned} P(1) = 7826 \mod 6301 = 1525 &\Rightarrow key_1 = (1, 1525) \\ P(2) = 10265 \mod 6301 = 3964 &\Rightarrow key_2 = (2, 3964) \\ P(3) = 13204 \mod 6301 = 602 &\Rightarrow key_3 = (3, 602) \\ P(4) = 16643 \mod 6301 = 4041 &\Rightarrow key_4 = (4, 4041) \\ P(5) = 20582 \mod 6301 = 1679 &\Rightarrow key_5 = (5, 1679) \end{aligned}

Lorsque nous fournirons les clés key1,key2,key4key_1, key_2, key_4, l’algorithme de récupération générera les mêmes polynômes de Lagrange mais le calcul du polynôme P changera comme suit:

P(X)=y0l0(X)+y1l1(X)+y2l2(X)mod6301=152531(X26X+8)396421(X25X+4)+404161(X23X+2)mod6301=(152531396421+404161mod6301)X2+(3050+1982021404121mod6301)X+(12200317928+404131mod6301)=250X2+1689X+5887\begin{aligned} P(X) &= y_0 l_0(X) + y_1 l_1(X) + y_2 l_2(X) \mod 6301 \\ &= 1525 \cdot 3^{-1} (X^2 - 6X + 8) - 3964 \cdot 2^{-1} (X^2 - 5X + 4) + 4041 \cdot 6^{-1} (X^2 - 3X + 2) \mod 6301 \\ &= (1525 \cdot 3^{-1} - 3964 \cdot 2^{-1} + 4041 \cdot 6^{-1} \mod 6301) X^2 + (- 3050 + 19820 \cdot 2^{-1} - 4041 \cdot 2^{-1} \mod 6301) X + (12200 \cdot 3^{-1} - 7928 + 4041 \cdot 3^{-1} \mod 6301) \\ &= 250 X^2 + 1689 X + 5887 \end{aligned}

Et nous voyons que nous sommes bien parvenus à retrouver le bon polynôme en utilisant l’arithmétique modulaire.

Comparaison avec l'approche naïve

Revenons à ce que nous avons mentionné au début de ce tutoriel quant aux défauts d’une approche naïve. Avec l’algorithme de Shamir, nous pouvons générer autant de fragments que l’on souhaite et nous pouvons définir un seuil de fragments nécessaires à la récupération du code. Donc nous ne craignons plus qu’un fragments soit perdu par mégarde : n’importe quelle combinaison de fragments, tant qu’il y en assez, nous permettra de récupérer le code initial.

Regardons maintenant le second et principal défaut de l’approche naïve : cette approche nous donnait de plus en plus d’information au fur et à mesure que l’on combinait les fragments ensemble. Or ce que nous cherchons est une approche de tout ou rien : soit nous n’avons pas assez de fragments et je ne peux rien dire quant au code initial, soit j’en ai assez et je peux récupérer ce code.

Ainsi, pour pouvoir affirmer que notre algorithme est sûr, nous devons montrer que si quelqu’un possède au plus k1k-1 clés, il n’est pas plus proche de récupérer le message secret qu’une personne n’en ayant aucune1.

Heureusement, pour notre algorithme, c’est bel et bien le cas ! En effet, avec k1k-1 clés, le hacker connaitrait k1k-1 points par lesquels notre polynôme passe mais nous savons que notre polynôme est de degré k1k - 1. Or, dans Zp\mathbb Z_p, il existe pp polynômes passant par ces k1k-1 points, correspondant à autant de a0a_0 distincts (c’est-à-dire à des codes secrets distincts). Ainsi, même avec k1k-1 points, on en est au même point qu’un hacker sans le moindre fragment: chacun des codes dans Zp\mathbb Z_p est équiprobable. Le hacker n’a ainsi gagné aucune information sur le polynôme utilisé pour créer les clés et notre algorithme est bel et bien correct et sûr.

La sécurité de l’algorithme repose également sur le fait que lorsque nous choisissons les paramètres du polynôme lors de la génération des clés, nous choisissons ceux-ci de façon aléatoires dans Zp\mathbb Z_p. En pratique, cette contrainte n’est pas aussi simple à réaliser qu’il n’y parait mais l’étude de la génération de nombres aléatoires est trop vaste et complexe pour être abordée dans le cadre de ce tutoriel.


  1. Si vous voulez une explication plus en profondeur sur cette définition de la sécurité d’un algorithme, vous pouvez lire cet article de Bermudes.

Voilà, grâce à la méthode de Shamir, nous avons maintenant une méthode nous permettant de partager une clé secrète entre plusieurs personnes, sans compromettre la sécurité de notre système. Donc si vous avez un coffre-fort ou une bombe nucléaire en votre possession, vous êtes maintenant en mesure de partager leur code à plusieurs proches de confiance. En espérant qu’ils en fassent bonne utilisation.

Si vous voulez approfondir le sujet, je vous invite à visiter la page Wikipedia. Malheureusement, la page francophone est assez difficile à suivre donc je vous invite plutôt à vous diriger vers la page anglophone qui est plutôt bien expliquée et qui contient même un exemple d’implémentation en Python.

Pour finir, un grand merci à @Lucas-84, @Gabbro et @melepe pour leurs retours durant la beta et la validation de ce tutoriel.

Ces contenus pourraient vous intéresser

3 commentaires

Super intéressant comme article merci !

C’est rigolo comme un concept relativement "simple" (faire passer un polynôme par n points) peut trouver des applications concrètes de ce type très utiles ! :)

+1 -0

Très belle publication.

Je salue particulièrement la pédagogie de ce tuto. Pour quelqu’un comme moi qui n’aime pas les formules de maths, j’ai réussi a suivre jusqu’au bout. ça n’a l’air de rien, mais le paragraphe sur le rappel des notations que tu utilise a été très utile pour la suite.

Merci encore pour le contenu de qualité.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte