-
Notifications
You must be signed in to change notification settings - Fork 168
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Based on the work from @lhmerino (https://github.com/dedis/votegral/blob/master/apps/Cothority/votegral/lib/crypto/shuffle.go)
- Loading branch information
Showing
5 changed files
with
363 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package examples | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
"go.dedis.ch/kyber/v3" | ||
kproof "go.dedis.ch/kyber/v3/proof" | ||
"go.dedis.ch/kyber/v3/shuffle" | ||
) | ||
|
||
/* | ||
This example illustrates how to use the Neff shuffle protocol with simple, | ||
single pairs. | ||
*/ | ||
func Test_Example_Neff_Shuffle_Simple(t *testing.T) { | ||
numPairs := 3 | ||
|
||
// generate random pairs | ||
|
||
ks := make([]kyber.Point, numPairs) | ||
cs := make([]kyber.Point, numPairs) | ||
|
||
for i := 0; i < numPairs; i++ { | ||
c := suite.Point().Mul(suite.Scalar().Pick(suite.RandomStream()), nil) | ||
k := suite.Point().Mul(suite.Scalar().Pick(suite.RandomStream()), nil) | ||
|
||
ks[i] = k | ||
cs[i] = c | ||
} | ||
|
||
// shuffle the pairs | ||
|
||
xx, yy, prover := shuffle.Shuffle(suite, nil, nil, ks, cs, suite.RandomStream()) | ||
|
||
// compute the proof | ||
|
||
proof, err := kproof.HashProve(suite, "PairShuffle", prover) | ||
require.NoError(t, err) | ||
|
||
// check the proof | ||
|
||
verifier := shuffle.Verifier(suite, nil, nil, ks, cs, xx, yy) | ||
|
||
err = kproof.HashVerify(suite, "PairShuffle", verifier, proof) | ||
require.NoError(t, err) | ||
} | ||
|
||
/* | ||
This example illustrates how to use the Neff shuffle protocol on sequences of | ||
pairs. The single pair protocol (see above) uses as inputs one-dimensional | ||
slices. This variation uses 2-dimensional slices, where the number of columns | ||
defines the number of sequences, and the number of rows defines the length of | ||
sequences. There is also a difference when getting the prover. In this variation | ||
the Shuffle function doesn't directly return a prover, but a function to get it. | ||
This is because the verifier must provide a slice of random numbers to the | ||
prover. | ||
*/ | ||
func Test_Example_Neff_Shuffle_Sequence(t *testing.T) { | ||
sequenceLen := 3 | ||
numSequences := 3 | ||
|
||
X := make([][]kyber.Point, numSequences) | ||
Y := make([][]kyber.Point, numSequences) | ||
|
||
// generate random sequences | ||
|
||
for i := 0; i < numSequences; i++ { | ||
xs := make([]kyber.Point, sequenceLen) | ||
ys := make([]kyber.Point, sequenceLen) | ||
|
||
for i := 0; i < sequenceLen; i++ { | ||
xs[i] = suite.Point().Mul(suite.Scalar().Pick(suite.RandomStream()), nil) | ||
ys[i] = suite.Point().Mul(suite.Scalar().Pick(suite.RandomStream()), nil) | ||
} | ||
|
||
X[i] = xs | ||
Y[i] = ys | ||
} | ||
|
||
// shuffle sequences | ||
|
||
XX, YY, getProver := shuffle.SequencesShuffle(suite, nil, nil, X, Y, suite.RandomStream()) | ||
|
||
// compute the proof | ||
|
||
NQ := len(X) | ||
e := make([]kyber.Scalar, NQ) | ||
for j := 0; j < NQ; j++ { | ||
e[j] = suite.Scalar().Pick(suite.RandomStream()) | ||
} | ||
|
||
prover, err := getProver(e) | ||
require.NoError(t, err) | ||
|
||
proof, err := kproof.HashProve(suite, "SequencesShuffle", prover) | ||
|
||
// check the proof | ||
|
||
XXUp, YYUp, XXDown, YYDown := shuffle.GetSequenceVerifiable(suite, X, Y, XX, YY, e) | ||
|
||
verifier := shuffle.Verifier(suite, nil, nil, XXUp, YYUp, XXDown, YYDown) | ||
|
||
err = kproof.HashVerify(suite, "SequencesShuffle", verifier, proof) | ||
require.NoError(t, err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package shuffle | ||
|
||
import ( | ||
"crypto/cipher" | ||
"fmt" | ||
|
||
"go.dedis.ch/kyber/v3" | ||
"go.dedis.ch/kyber/v3/proof" | ||
) | ||
|
||
// SequencesShuffle shuffles a sequence of ElGamal pairs based on Section 5 of | ||
// "Verifiable Mixing (Shuffling) of ElGamal Pairs" by Andrew Neff (April 2004) | ||
// | ||
// The function expects X and Y to be the same dimension, with each row having | ||
// the same length. It also expect X and Y to have at least one element. | ||
// | ||
// Dim X and Y: [<sequence length, j>, <number of sequences, i>] | ||
// | ||
// The number of rows defines the sequences length. The number of columns | ||
// defines the number of sequences. | ||
// | ||
// Seq 1 Seq 2 Seq 3 | ||
// (0,0) (0,1) (0,2) | ||
// (1,0) (1,1) (1,2) | ||
// (2,0) (2,1) (2,2) | ||
// | ||
// In the code coordinates are (j,i), where 1 ≤ i ≤ k, 1 ≤ j ≤ NQ | ||
// | ||
// Last coordinate is (NQ-1, k-1) | ||
// | ||
// Variable names are as representative to the paper as possible. Instead of | ||
// representing (variable name with a bar on top), such as (X with a bar on top) | ||
// with Xbar, we represent it with a repeating letter, such as XX | ||
func SequencesShuffle(group kyber.Group, g, h kyber.Point, X, Y [][]kyber.Point, | ||
rand cipher.Stream) (XX, YY [][]kyber.Point, getProver func(e []kyber.Scalar) ( | ||
proof.Prover, error)) { | ||
|
||
NQ := len(X) | ||
k := len(X[0]) | ||
|
||
// Pick a random permutation used in ALL k ElGamal sequences. The permutation | ||
// (π) of an ElGamal pair at index i always outputs to the same index | ||
pi := make([]int, k) | ||
for i := 0; i < k; i++ { | ||
pi[i] = i | ||
} | ||
|
||
// Fisher–Yates shuffle | ||
for i := k - 1; i > 0; i-- { | ||
j := int(randUint64(rand) % uint64(i+1)) | ||
if j != i { | ||
pi[i], pi[j] = pi[j], pi[i] | ||
} | ||
} | ||
|
||
// Pick a fresh ElGamal blinding factor β(j, i) for each ElGamal sequence | ||
// and each ElGamal pair | ||
beta := make([][]kyber.Scalar, NQ) | ||
for j := 0; j < NQ; j++ { | ||
beta[j] = make([]kyber.Scalar, k) | ||
for i := 0; i < k; i++ { | ||
beta[j][i] = group.Scalar().Pick(rand) | ||
} | ||
} | ||
|
||
// Perform the Shuffle | ||
|
||
XX = make([][]kyber.Point, NQ) | ||
YY = make([][]kyber.Point, NQ) | ||
|
||
for j := 0; j < NQ; j++ { | ||
XX[j] = make([]kyber.Point, k) | ||
YY[j] = make([]kyber.Point, k) | ||
|
||
for i := 0; i < k; i++ { | ||
XX[j][i] = group.Point().Mul(beta[j][pi[i]], g) | ||
XX[j][i].Add(XX[j][i], X[j][pi[i]]) | ||
|
||
YY[j][i] = group.Point().Mul(beta[j][pi[i]], h) | ||
YY[j][i].Add(YY[j][i], Y[j][pi[i]]) | ||
} | ||
} | ||
|
||
getProver = func(e []kyber.Scalar) (proof.Prover, error) { | ||
// EGAR 2 (Prover) - Standard ElGamal k-shuffle proof: Knowledge of | ||
// (XXUp, YYUp), (XXDown, YYDown) and e[j] | ||
|
||
ps := PairShuffle{} | ||
ps.Init(group, k) | ||
|
||
if len(e) != NQ { | ||
return nil, fmt.Errorf("len(e) must be equal to NQ: %d != %d", len(e), NQ) | ||
} | ||
|
||
return func(ctx proof.ProverContext) error { | ||
// Need to consolidate beta to a one dimensional array | ||
beta2 := make([]kyber.Scalar, k) | ||
|
||
for i := 0; i < k; i++ { | ||
beta2[i] = group.Scalar().Mul(e[0], beta[0][i]) | ||
|
||
for j := 1; j < NQ; j++ { | ||
beta2[i] = group.Scalar().Add(beta2[i], | ||
group.Scalar().Mul(e[j], beta[j][i])) | ||
} | ||
} | ||
|
||
XXUp, YYUp, _, _ := GetSequenceVerifiable(group, X, Y, XX, YY, e) | ||
|
||
return ps.Prove(pi, g, h, beta2, XXUp, YYUp, rand, ctx) | ||
}, nil | ||
} | ||
|
||
return XX, YY, getProver | ||
} | ||
|
||
// GetSequenceVerifiable returns the consolidated input and output of sequence | ||
// shuffling elements. Needed by the prover and verifier. | ||
func GetSequenceVerifiable(group kyber.Group, X, Y, XX, YY [][]kyber.Point, e []kyber.Scalar) ( | ||
XXUp, YYUp, XXDown, YYDown []kyber.Point) { | ||
|
||
// EGAR1 (Verifier) - Consolidate input and output | ||
|
||
NQ := len(X) | ||
k := len(X[0]) | ||
|
||
XXUp = make([]kyber.Point, k) | ||
YYUp = make([]kyber.Point, k) | ||
XXDown = make([]kyber.Point, k) | ||
YYDown = make([]kyber.Point, k) | ||
|
||
for i := 0; i < k; i++ { | ||
// No modification could be made for e[0] -> e[0] = 1 if one wanted - | ||
// Remark 7 in the paper | ||
XXUp[i] = group.Point().Mul(e[0], X[0][i]) | ||
YYUp[i] = group.Point().Mul(e[0], Y[0][i]) | ||
|
||
XXDown[i] = group.Point().Mul(e[0], XX[0][i]) | ||
YYDown[i] = group.Point().Mul(e[0], YY[0][i]) | ||
|
||
for j := 1; j < NQ; j++ { | ||
XXUp[i] = group.Point().Add(XXUp[i], | ||
group.Point().Mul(e[j], X[j][i])) | ||
YYUp[i] = group.Point().Add(YYUp[i], | ||
group.Point().Mul(e[j], Y[j][i])) | ||
|
||
XXDown[i] = group.Point().Add(XXDown[i], | ||
group.Point().Mul(e[j], XX[j][i])) | ||
YYDown[i] = group.Point().Add(YYDown[i], | ||
group.Point().Mul(e[j], YY[j][i])) | ||
} | ||
} | ||
|
||
return XXUp, YYUp, XXDown, YYDown | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.