@@ -99,42 +99,90 @@ func div(a, b uint8) uint8 {
9999 panic ("divide by zero" )
100100 }
101101
102+ // a divided by b is the same as a multiplied by the inverse of b:
102103 return mult (a , inverse (b ))
103104}
104105
105106// inverse calculates the inverse of a number in GF(2^8)
107+ // Note that a must be non-zero; otherwise 0 is returned
106108func inverse (a uint8 ) uint8 {
107- b := mult (a , a )
108- c := mult (a , b )
109- b = mult (c , c )
110- b = mult (b , b )
111- c = mult (b , c )
112- b = mult (b , b )
113- b = mult (b , b )
114- b = mult (b , c )
115- b = mult (b , b )
116- b = mult (a , b )
117-
118- return mult (b , b )
109+ // This makes use of Fermat's Little Theorem for finite groups:
110+ // If G is a finite group with n elements, and a any element of G,
111+ // then a raised to the power of n equals the neutral element of G.
112+ // (See https://en.wikipedia.org/wiki/Fermat%27s_little_theorem;
113+ // the generalization to finite groups follows from Lagrange's theorem:
114+ // https://en.wikipedia.org/wiki/Lagrange%27s_theorem_(group_theory))
115+ //
116+ // Here we use the multiplicative group of GF(2^8), which has
117+ // n = 2^8 - 1 elements (every element but zero). Thus raising a to
118+ // the (n - 1)th = 254th power gives a number x so that a*x = 1.
119+ //
120+ // If a happens to be 0, which is not part of the multiplicative group,
121+ // then a raised to the power of 254 is still 0.
122+
123+ // (See also https://github.com/openbao/openbao/commit/a209a052024b70bc563d9674cde21a20b5106570)
124+
125+ // In the comments, we use ^ to denote raising to the power:
126+ b := mult (a , a ) // b is now a^2
127+ c := mult (a , b ) // c is now a^3
128+ b = mult (c , c ) // b is now a^6
129+ b = mult (b , b ) // b is now a^12
130+ c = mult (b , c ) // c is now a^15
131+ b = mult (b , b ) // b is now a^24
132+ b = mult (b , b ) // b is now a^48
133+ b = mult (b , c ) // b is now a^63
134+ b = mult (b , b ) // b is now a^126
135+ b = mult (a , b ) // b is now a^127
136+ return mult (b , b ) // result is a^254
119137}
120138
121139// mult multiplies two numbers in GF(2^8)
122140// GF(2^8) multiplication using log/exp tables
123141func mult (a , b uint8 ) (out uint8 ) {
124- var r uint8 = 0
142+ // This computes a * b in GF(2^8), which is defined as GF(2)[X] / <X^8 + X^4 + X^3 + X + 1>.
143+ // This finite field is known as Rijndael's finite field. (Rijndael is the algorithm that
144+ // was standardized as AES.)
145+ // (See https://en.wikipedia.org/wiki/Finite_field_arithmetic#Rijndael's_(AES)_finite_field)
146+ //
147+ // We identify elements in GF(2^8) with polynomials of degree < 8. The i-th bit of a field
148+ // element is the coefficient of X^i in that polynomial.
149+ //
150+ // To multiply a and b in this finite field, we use something similar to Russian peasant
151+ // multiplication. We iterate over b's bits, starting from the highest to the lowest.
152+ // i denotes the bit we're currently processing (7, 6, 5, 4, 3, 2, 1, 0).
153+ // The accumulator is set to 0; every iteration, we multiply the accumulator
154+ // by X modulo X^8+X^4+X^3+X+1, and then add a to the accumulator in case b's i-th bit is 1.
155+ var accumulator uint8 = 0
125156 var i uint8 = 8
126157
127158 for i > 0 {
128159 i --
129- r = (- (b >> i & 1 ) & a ) ^ (- (r >> 7 ) & 0x1B ) ^ (r + r )
160+ // Get the i-th bit of b; bitOfB is either 0 or 1.
161+ bitOfB := b >> i & 1
162+ // aOrZero is 0 if the i-th bit of b is 0, and a if the i-th bit of b is 1. This is
163+ // what we later add to the accumulator.
164+ aOrZero := - bitOfB & a
165+ // zeroOr1B is 0 if the 7th bit of the accumulator is 0, and 0x1B = 11011_2 if the
166+ // 7th bit of accumulator is 1
167+ zeroOr1B := - (accumulator >> 7 ) & 0x1B
168+ // accumulatorMultipliedByX equals accumulator multiplied by X modulo X^8+X^4+X^3+X+1
169+ // In the expression, accumulator + accumulator equals accumulator << 1, which would be
170+ // the accumulator multiplied by X modulo X^8.
171+ // By XORing (addition and subtraction in GF(2^8)) with zeroOr1B, we turn this into
172+ // accumulator multiplied by X modulo X^8 + X^4 + X^3 + X + 1.
173+ accumulatorMultipliedByX := zeroOr1B ^ (accumulator + accumulator )
174+ // We can now compute the next value of the accumulator as the sum (in GF(2^8)) of aOrZero
175+ // and accumulatorMultipliedByX.
176+ accumulator = aOrZero ^ accumulatorMultipliedByX
130177 }
131178
132- return r
179+ return accumulator
133180}
134181
135182// add combines two numbers in GF(2^8)
136183// This can also be used for subtraction since it is symmetric.
137184func add (a , b uint8 ) uint8 {
185+ // Addition in GF(2^8) equals XOR:
138186 return a ^ b
139187}
140188
0 commit comments