ceremonyclient/node/crypto/shuffle/matrix.go
Cassandra Heart 0cd6b41f5d
v1.2.4 (#43)
2024-02-13 01:04:56 -06:00

469 lines
11 KiB
Go

package shuffle
import (
"crypto/rand"
"math/big"
"filippo.io/edwards25519"
)
var lBE = []byte{
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 222, 249, 222, 162, 247,
156, 214, 88, 18, 99, 26, 92, 245, 211, 236,
}
var lBigInt = big.NewInt(0).SetBytes(lBE)
func genPolyFrags(
secret *edwards25519.Scalar,
total, threshold int,
) []*edwards25519.Scalar {
coeffs := []*edwards25519.Scalar{}
coeffs = append(coeffs, secret)
for i := 1; i < threshold; i++ {
coeffBI, _ := rand.Int(rand.Reader, lBigInt)
coeff := BigIntToLEBytes(coeffBI)
scalar, err := edwards25519.NewScalar().SetCanonicalBytes(coeff[:])
if err != nil {
panic(err)
}
coeffs = append(coeffs, scalar)
}
frags := []*edwards25519.Scalar{}
for i := 1; i <= total; i++ {
result, _ := edwards25519.NewScalar().SetCanonicalBytes(coeffs[0].Bytes())
iBytes := BigIntToLEBytes(big.NewInt(int64(i)))
x, err := edwards25519.NewScalar().SetCanonicalBytes(iBytes)
if err != nil {
panic(err)
}
for j := 1; j <= threshold-1; j++ {
xi := edwards25519.NewScalar().Multiply(coeffs[j], x)
result.Add(result, xi)
xmul, _ := edwards25519.NewScalar().SetCanonicalBytes(iBytes)
x.Multiply(x, xmul)
}
frags = append(frags, result)
}
return frags
}
func ShamirSplitMatrix(
matrix [][]*edwards25519.Scalar,
total, threshold int,
) [][][]*edwards25519.Scalar {
shamirMatrix := make([][][]*edwards25519.Scalar, len(matrix))
for x := 0; x < len(matrix); x++ {
shamirMatrix[x] = make([][]*edwards25519.Scalar, len(matrix[0]))
for y := 0; y < len(matrix[0]); y++ {
shamirMatrix[x][y] = genPolyFrags(matrix[x][y], total, threshold)
}
}
return shamirMatrix
}
func AddMatrices(matrices ...[][]*edwards25519.Scalar) [][]*edwards25519.Scalar {
result := make([][]*edwards25519.Scalar, len(matrices[0]))
for x := 0; x < len(matrices[0]); x++ {
result[x] = make([]*edwards25519.Scalar, len(matrices[0][0]))
for y := 0; y < len(matrices[0][0]); y++ {
result[x][y] = edwards25519.NewScalar()
for i := 0; i < len(matrices); i++ {
result[x][y].Add(result[x][y], matrices[i][x][y])
}
}
}
return result
}
func GenerateRandomVectorShares(
length, total, threshold int,
) [][]*edwards25519.Scalar {
result := make([][]*edwards25519.Scalar, length)
for i := 0; i < length; i++ {
bi, _ := rand.Int(rand.Reader, lBigInt)
biBytes := BigIntToLEBytes(bi)
scalar, _ := edwards25519.NewScalar().SetCanonicalBytes(biBytes[:])
result[i] = genPolyFrags(scalar, total, threshold)
}
return result
}
func InterpolatePolynomialShares(
shares []*edwards25519.Scalar,
ids []int,
) *edwards25519.Scalar {
var reconstructedSum *edwards25519.Scalar
for j := 0; j < len(ids); j++ {
oneLENumBytes := BigIntToLEBytes(big.NewInt(1))
coeffNum, _ := edwards25519.NewScalar().SetCanonicalBytes(oneLENumBytes)
coeffDenom, _ := edwards25519.NewScalar().SetCanonicalBytes(oneLENumBytes)
for k := 0; k < len(ids); k++ {
if j != k {
ikBytes := BigIntToLEBytes(big.NewInt(int64(ids[k])))
ijBytes := BigIntToLEBytes(big.NewInt(int64(ids[j])))
ikScalar, _ := edwards25519.NewScalar().SetCanonicalBytes(ikBytes)
ijScalar, _ := edwards25519.NewScalar().SetCanonicalBytes(ijBytes)
coeffNum.Multiply(coeffNum, ikScalar)
ikScalar.Subtract(ikScalar, ijScalar)
coeffDenom.Multiply(coeffDenom, ikScalar)
}
}
coeffDenom.Invert(coeffDenom)
coeffNum.Multiply(coeffNum, coeffDenom)
reconstructedFrag := edwards25519.NewScalar().Multiply(
coeffNum,
shares[ids[j]-1],
)
if reconstructedSum == nil {
reconstructedSum = reconstructedFrag
} else {
reconstructedSum.Add(reconstructedSum, reconstructedFrag)
}
}
return reconstructedSum
}
func LUDecompose(
matrix [][]*edwards25519.Scalar,
) ([][]*edwards25519.Scalar, [][]*edwards25519.Scalar) {
imax := 0
maxA := edwards25519.NewScalar()
N := len(matrix)
p := make([]int, N)
pm := make([][]*edwards25519.Scalar, N)
newA := make([][]*edwards25519.Scalar, N)
for i := 0; i < N; i++ {
newA[i] = make([]*edwards25519.Scalar, N)
pm[i] = make([]*edwards25519.Scalar, N)
p[i] = i
for j := 0; j < N; j++ {
newA[i][j], _ = edwards25519.NewScalar().SetCanonicalBytes(
matrix[i][j].Bytes(),
)
}
}
scalarOne, _ := edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(int64(1))),
)
for i := 0; i < N; i++ {
maxA = edwards25519.NewScalar()
imax = i
for k := i; k < N; k++ {
if LEBytesToBigInt(newA[k][i].Bytes()).Cmp(
LEBytesToBigInt(maxA.Bytes()),
) > 0 {
maxA = newA[k][i]
imax = k
}
}
if imax != i {
//pivoting P
j := p[i]
p[i] = p[imax]
p[imax] = j
//pivoting rows of A
ptr := newA[i]
newA[i] = newA[imax]
newA[imax] = ptr
}
for j := i + 1; j < N; j++ {
newA[j][i].Multiply(
newA[j][i],
edwards25519.NewScalar().Invert(newA[i][i]),
)
for k := i + 1; k < N; k++ {
newA[j][k].Subtract(newA[j][k], edwards25519.NewScalar().Multiply(
newA[j][i],
newA[i][k],
))
}
}
}
for i := 0; i < N; i++ {
for j := 0; j < N; j++ {
if p[i] == j {
pm[i][j] = scalarOne
} else {
pm[i][j] = edwards25519.NewScalar()
}
}
}
return newA, pm
}
func Invert(matrix [][]*edwards25519.Scalar) [][]*edwards25519.Scalar {
a, p := LUDecompose(matrix)
ia := make([][]*edwards25519.Scalar, len(matrix))
for i := 0; i < len(matrix); i++ {
ia[i] = make([]*edwards25519.Scalar, len(matrix))
}
for j := 0; j < len(matrix); j++ {
for i := 0; i < len(matrix); i++ {
ia[i][j] = edwards25519.NewScalar().Set(p[i][j])
for k := 0; k < i; k++ {
ia[i][j].Subtract(ia[i][j], edwards25519.NewScalar().Multiply(
a[i][k],
ia[k][j],
))
}
}
for i := len(matrix) - 1; i >= 0; i-- {
for k := i + 1; k < len(matrix); k++ {
ia[i][j].Subtract(ia[i][j], edwards25519.NewScalar().Multiply(
a[i][k],
ia[k][j],
))
}
ia[i][j].Multiply(ia[i][j], edwards25519.NewScalar().Invert(a[i][i]))
}
}
return ia
}
func InterpolateMatrixShares(
matrixShares [][][]*edwards25519.Scalar,
ids []int,
) [][]*edwards25519.Scalar {
matrix := make([][]*edwards25519.Scalar, len(matrixShares))
for x := 0; x < len(matrix); x++ {
matrix[x] = make([]*edwards25519.Scalar, len(matrixShares[0]))
for y := 0; y < len(matrix[0]); y++ {
matrix[x][y] = InterpolatePolynomialShares(matrixShares[x][y], ids)
}
}
return matrix
}
func ScalarMult(a int, b [][]*edwards25519.Scalar) [][]*edwards25519.Scalar {
prod := make([][]*edwards25519.Scalar, len(b))
for x := 0; x < len(b); x++ {
prod[x] = make([]*edwards25519.Scalar, len(b[0]))
for y := 0; y < len(b[0]); y++ {
if a >= 0 {
prod[x][y], _ = edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(int64(a))),
)
} else {
negA, _ := edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(int64(-a))),
)
prod[x][y] = edwards25519.NewScalar().Subtract(
edwards25519.NewScalar(),
negA,
)
}
prod[x][y] = prod[x][y].Multiply(prod[x][y], b[x][y])
}
}
return prod
}
func GenerateDotProduct(
a, b [][]*edwards25519.Scalar,
) [][]*edwards25519.Scalar {
if len(a[0]) != len(b) {
panic("cannot generate dot product of a and b - mismatched length")
}
abMatrix := make([][]*edwards25519.Scalar, len(a))
for x := 0; x < len(a); x++ {
abMatrix[x] = make([]*edwards25519.Scalar, len(b[0]))
for y := 0; y < len(b[0]); y++ {
abMatrix[x][y] = edwards25519.NewScalar()
for ay := 0; ay < len(a[0]); ay++ {
abMatrix[x][y].MultiplyAdd(a[x][ay], b[ay][y], abMatrix[x][y])
}
}
}
return abMatrix
}
func GenerateRandomMatrixAndInverseShares(
size, total, threshold int,
) [2][][][]*edwards25519.Scalar {
output := make([][]*edwards25519.Scalar, size)
for x := 0; x < size; x++ {
output[x] = make([]*edwards25519.Scalar, size)
for y := 0; y < size; y++ {
i, _ := rand.Int(rand.Reader, lBigInt)
iBytes := BigIntToLEBytes(i)
iScalar, _ := edwards25519.NewScalar().SetCanonicalBytes(iBytes[:])
output[x][y] = iScalar
}
}
splitOutput := ShamirSplitMatrix(output, total, threshold)
splitInverse := ShamirSplitMatrix(Invert(output), total, threshold)
return [2][][][]*edwards25519.Scalar{splitOutput, splitInverse}
}
func GenerateRandomBeaverTripleMatrixShares(
size, total, threshold int,
) [3][][][]*edwards25519.Scalar {
uMatrix := make([][]*edwards25519.Scalar, size)
vMatrix := make([][]*edwards25519.Scalar, size)
for i := 0; i < size; i++ {
uMatrix[i] = make([]*edwards25519.Scalar, size)
vMatrix[i] = make([]*edwards25519.Scalar, size)
for j := 0; j < size; j++ {
uj, _ := rand.Int(rand.Reader, lBigInt)
ujBytes := BigIntToLEBytes(uj)
ujScalar, _ := edwards25519.NewScalar().SetCanonicalBytes(ujBytes[:])
vj, _ := rand.Int(rand.Reader, lBigInt)
vjBytes := BigIntToLEBytes(vj)
vjScalar, _ := edwards25519.NewScalar().SetCanonicalBytes(vjBytes[:])
uMatrix[i][j] = ujScalar
vMatrix[i][j] = vjScalar
}
}
uvMatrix := GenerateDotProduct(uMatrix, vMatrix)
uMatrixShares := ShamirSplitMatrix(uMatrix, total, threshold)
vMatrixShares := ShamirSplitMatrix(vMatrix, total, threshold)
uvMatrixShares := ShamirSplitMatrix(uvMatrix, total, threshold)
return [3][][][]*edwards25519.Scalar{
uMatrixShares, vMatrixShares, uvMatrixShares,
}
}
func GeneratePermutationMatrix(size int) [][]*edwards25519.Scalar {
matrix := [][]*edwards25519.Scalar{}
elements := []int{}
for i := 0; i < size; i++ {
elements = append(elements, i)
}
for i := 0; i < size; i++ {
pos, _ := rand.Int(rand.Reader, big.NewInt(int64(len(elements))))
var vecPos int
elements, vecPos = remove(elements, int(pos.Int64()))
scalarOne, err := edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(1)),
)
if err != nil {
panic(err)
}
vector := []*edwards25519.Scalar{}
for j := 0; j < vecPos; j++ {
scalarZero, err := edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(0)),
)
if err != nil {
panic(err)
}
vector = append(vector, scalarZero)
}
vector = append(vector, scalarOne)
for j := vecPos + 1; j < size; j++ {
scalarZero, err := edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(0)),
)
if err != nil {
panic(err)
}
vector = append(vector, scalarZero)
}
matrix = append(matrix, vector)
}
return matrix
}
func BigIntToLEBytes(bi *big.Int) []byte {
b := bi.Bytes()
last := len(b) - 1
for i := 0; i < len(b)/2; i++ {
b[i], b[last-i] = b[last-i], b[i]
}
for i := len(b); i < 32; i++ {
b = append(b, 0x00)
}
return b
}
func LEBytesToBigInt(bytes []byte) *big.Int {
b := make([]byte, len(bytes))
last := len(b) - 1
for i := 0; i < len(b)/2; i++ {
b[i], b[last-i] = b[last-i], b[i]
}
res := big.NewInt(0)
return res.SetBytes(b)
}
func remove(elements []int, i int) ([]int, int) {
ret := elements[i]
elements[i] = elements[len(elements)-1]
newElements := []int{}
newElements = append(newElements, elements[:len(elements)-1]...)
return newElements, ret
}