@@ -12,9 +12,7 @@ package shamir
1212
1313import (
1414 "crypto/rand"
15- "crypto/subtle"
1615 "fmt"
17- mathrand "math/rand"
1816)
1917
2018const (
@@ -101,63 +99,90 @@ func div(a, b uint8) uint8 {
10199 panic ("divide by zero" )
102100 }
103101
104- var goodVal , zero uint8
105- logA := logTable [a ]
106- logB := logTable [b ]
107- diff := (int (logA ) - int (logB )) % 255
108- if diff < 0 {
109- diff += 255
110- }
111-
112- ret := expTable [diff ]
113-
114- // Ensure we return zero if a is zero but aren't subject to timing attacks
115- goodVal = ret
116-
117- if subtle .ConstantTimeByteEq (a , 0 ) == 1 {
118- ret = zero
119- } else {
120- ret = goodVal
121- }
102+ // a divided by b is the same as a multiplied by the inverse of b:
103+ return mult (a , inverse (b ))
104+ }
122105
123- return ret
106+ // inverse calculates the inverse of a number in GF(2^8)
107+ // Note that a must be non-zero; otherwise 0 is returned
108+ func inverse (a uint8 ) uint8 {
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
124137}
125138
126139// mult multiplies two numbers in GF(2^8)
127140// GF(2^8) multiplication using log/exp tables
128141func mult (a , b uint8 ) (out uint8 ) {
129- var goodVal , zero uint8
130- log_a := logTable [a ]
131- log_b := logTable [b ]
132- sum := (int (log_a ) + int (log_b )) % 255
133-
134- ret := expTable [sum ]
135-
136- // Ensure we return zero if either a or b are zero but aren't subject to
137- // timing attacks
138- goodVal = ret
139-
140- if subtle .ConstantTimeByteEq (a , 0 ) == 1 {
141- ret = zero
142- } else {
143- ret = goodVal
144- }
145-
146- if subtle .ConstantTimeByteEq (b , 0 ) == 1 {
147- ret = zero
148- } else {
149- // This operation does not do anything logically useful. It
150- // only ensures a constant number of assignments to thwart
151- // timing attacks.
152- goodVal = zero
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
156+ var i uint8 = 8
157+
158+ for i > 0 {
159+ i --
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
153177 }
154178
155- return ret
179+ return accumulator
156180}
157181
158182// add combines two numbers in GF(2^8)
159183// This can also be used for subtraction since it is symmetric.
160184func add (a , b uint8 ) uint8 {
185+ // Addition in GF(2^8) equals XOR:
161186 return a ^ b
162187}
163188
@@ -184,13 +209,6 @@ func Split(secret []byte, parts, threshold int) ([][]byte, error) {
184209 return nil , fmt .Errorf ("cannot split an empty secret" )
185210 }
186211
187- // Generate random x coordinates for computing points. I don't know
188- // why random x coordinates are used, and I also don't know why
189- // a non-cryptographically secure source of randomness is used.
190- // As far as I know the x coordinates do not need to be random.
191-
192- xCoordinates := mathrand .Perm (255 )
193-
194212 // Allocate the output array, initialize the final byte
195213 // of the output with the offset. The representation of each
196214 // output is {y1, y2, .., yN, x}.
@@ -201,7 +219,7 @@ func Split(secret []byte, parts, threshold int) ([][]byte, error) {
201219 // then the result of evaluating the polynomial at that point
202220 // will be our secret
203221 out [idx ] = make ([]byte , len (secret )+ 1 )
204- out [idx ][len (secret )] = uint8 (xCoordinates [ idx ] ) + 1
222+ out [idx ][len (secret )] = uint8 (idx ) + 1
205223 }
206224
207225 // Construct a random polynomial for each byte of the secret.
@@ -222,7 +240,7 @@ func Split(secret []byte, parts, threshold int) ([][]byte, error) {
222240 for i := 0 ; i < parts ; i ++ {
223241 // Add 1 to the xCoordinate because if it's 0,
224242 // then the result of p.evaluate(x) will be our secret
225- x := uint8 (xCoordinates [ i ] ) + 1
243+ x := uint8 (i ) + 1
226244 // Evaluate the polynomial at x
227245 y := p .evaluate (x )
228246 out [i ][idx ] = y
0 commit comments