Réduction de Barrett

De testwiki
Aller à la navigation Aller à la recherche

En arithmétique modulaire, la réduction de Barrett est un algorithme de réduction introduit en 1986 par Paul D. Barrett[1]. Une façon naïve de calculer :

c=amodn

serait d'utiliser un algorithme de division rapide ; la réduction de Barrett est un algorithme conçu pour optimiser cette opération en supposant n constant et a<n2, remplaçant les divisions par des multiplications.

Idée générale

Soit s=1/n l'inverse de n comme nombre à virgule flottante. Alors

amodn=aasn

x est la fonction partie entière. Le résultat est exact, tant que s est calculé avec une précision suffisante.

Réduction de Barrett à un mot machine

Barrett a initialement considéré une version de l'algorithme ci-dessus sur les nombres entiers qui tiennent dans un seul mot machine.

En calculant amodn avec la méthode ci-dessus, mais avec des entiers, l'analogue évident serait la division par n:

func reduire(a uint) uint {
  q := a / n  // La division retourne implicitement la partie entière du résultat
  return a - q * n
}

Cependant, la division est lente et, dans les algorithmes de cryptographie, peut ne pas être une opération en temps constant sur certains processeurs. Ainsi, la réduction de Barrett approxime 1/n par la valeur m/2k, car la division par 2k est juste un décalage de bits vers la droite, donc est très rapide.

Afin de calculer la meilleure valeur pour m étant donné 2k, on considère :

m2k=1nm=2kn

Pour que m soit un entier, on a besoin d'arrondir 2k/n d'une certaine manière. Arrondir à l'entier le plus proche donnerait la meilleure approximation mais peut faire que m/2k soit plus grand que 1/n, qui peut provoquer un soupassement arithmétique. Ainsi, m=2k/n est généralement utilisé.

La fonction reduire ci-dessus peut donc être approximée par :

func reduire(a uint) uint {
  q := (a * m) >> k // ">> k" est le décalage de k bits vers la droite.
  return a - q * n
}

Cependant, puisque m/2k1/n, la valeur de q dans cette fonction peut être un peu trop petite, et ainsi a est seulement garantie d'être dans [0,2n[ plutôt que [0,n) comme c'est généralement requis. Une soustraction conditionnelle peut corriger cela :

func reduire(a uint) uint {
  q := (a * m) >> k
  a -= q * n
  if n <= a {
    a -= n
  }
  return a
}

Puisque m/2k est seulement une approximation, l'intervalle de validité de a doit être considéré. L'erreur sur l'approximation de 1/n est:

e=1nm2k

Ainsi, l'erreur sur la valeur de q est ae. Tant que ae<1, la réduction sera valide, d'où a<1/e. La fonction de réduction peut ne pas donner immédiatement un mauvais résultat quand a1/e mais la borne sur a doit être respectée pour garantir une réponse correcte dans le cas général.

En choisissant de plus grandes valeurs de k, l'intervalle des valeurs de a pour lesquelles la réduction est valide peut-être augmentée, mais de plus grandes valeurs de k peut causer des dépassements d'entiers.

Exemple

Regardons le cas n=101 en utilisant des entiers de 16 bits.

La plus petite valeur de k qui a du sens est k=7 car si 2k<n alors la réduction ne sera valide que pour des valeurs qui sont déjà minimales ! Pour une valeur de sept, m=2k/n=128/101=1. Pour une valeur de huit m=256/101=2. Ainsi k=8 ne donne aucun avantage car l'approximation de 1/101, dans ce cas 2/256, est exactement la même que 1/128. Pour k=9, on obtient m=512/101=5, qui donne une meilleure approximation.

Maintenant, considérons les intervalles de validité pour k=7 et k=9.

Dans le premier cas, e=1/nm/2k=1/1011/128=27/12928 donc a<1/e implique a<478.81. Puisque a est un entier, la valeur maximum effective est 478. (En pratique la fonction donne le bon résultat jusque 504.)

Si on prend k=9 alors e=1/1015/512=7/51712 et donc la valeur maximum de a est 7387. (En pratique la fonction donne le bon résultat jusque 7473.)

La valeur suivante de k qui donne une meilleure approximation est 13, donnant 81/8192. Cependant, notons que la valeur intermédiaire ax dans les calculs va dépasser la capacité d'un entier 16 bits non-signé lorsque 810a, ainsi k=7 fonctionne mieux dans cette situation.

Preuve de l'intervalle de validité pour un certain k

Soit k0 le plus petit entier tel que 2k0>n. Prenons k0+1 comme valeur raisonnable pour k dans les équations précédentes. Comme dans les fragments de code ci-dessus, soit

  • q=ma2k et
  • r=aqn.

Grâce à la fonction partie entière, q est un entier et ra(modn). Aussi, si a<2k alors r<2n. Dans ce cas, en écrivant le fragment de code en expression :

amodn={rsi r<nrnsinon

La preuve que r<2n est alors immédiate :

Si a<2k, alors

a2k(2kmodn)<n

Puisque nmamod2k2k<n indépendamment de a, on obtient :

a(2kmodn)2k+nmamod2k2k<2n
a(aa(2kmodn)2k)+n(mamod2k)2k<2n
aa2k(2k(2kmodn))+n(mamod2k)2k<2n
ana2k(2k(2kmodn)n)+n(mamod2k)2k<2n
ana2k2kn +n(mamod2k)2k<2n
anma2k+n(mamod2k)2k<2n
a(ma(mamod2k)2k)n<2n
ama2kn<2n
aqn<2n
r<2n

Réduction de Barrett à plusieurs mots machine

La motivation initiale de Barrett pour sa réduction était l'implémentation de RSA, lorsque les valeurs en question dépassent la taille d'un mot machine. Dans cette situation, Barrett donne un algorithme qui approxime la version à un mot ci-dessus mais pour des valeurs sur plusieurs mots. Pour plus de détails, voir la section 14.3.3 de Handbook of Applied Cryptography[2].

Références

Modèle:Traduction/Référence Modèle:Références

Voir aussi

Sources

Modèle:Portail