Skip to content

Commit 1c4635a

Browse files
committed
Better document arithmetic functions.
While writing these comments I noticed that OpenBao added similar comments roughly a year ago: openbao/openbao@a209a05 Signed-off-by: Felix Fontein <felix@fontein.de>
1 parent eddd8c5 commit 1c4635a

1 file changed

Lines changed: 63 additions & 15 deletions

File tree

shamir/shamir.go

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -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
106108
func 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
123141
func 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.
137184
func add(a, b uint8) uint8 {
185+
// Addition in GF(2^8) equals XOR:
138186
return a ^ b
139187
}
140188

0 commit comments

Comments
 (0)