-
Notifications
You must be signed in to change notification settings - Fork 169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds neff shuffling of sequences #457
Changes from 8 commits
5cea1f4
190450e
6ef3438
000a0de
4b7ca53
77126cd
b15d2be
9e13109
7c3589f
f17d5e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should use MixedCaps instead of underscores, but others examples use underscores too, so I guess that's fine. Other examples uses |
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd assign the point directly to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bad practice, in my opinion. It makes the code less readable. |
||
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 j := 0; j < sequenceLen; j++ { | ||
xs[j] = suite.Point().Mul(suite.Scalar().Pick(suite.RandomStream()), nil) | ||
ys[j] = 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++ { | ||
ineiti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
e[j] = suite.Scalar().Pick(suite.RandomStream()) | ||
} | ||
|
||
prover, err := getProver(e) | ||
require.NoError(t, err) | ||
|
||
proof, err := kproof.HashProve(suite, "SequencesShuffle", prover) | ||
require.NoError(t, err) | ||
|
||
nkcr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// 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) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package shuffle | ||
|
||
import ( | ||
"crypto/cipher" | ||
"fmt" | ||
"math/big" | ||
|
||
"go.dedis.ch/kyber/v3" | ||
"go.dedis.ch/kyber/v3/proof" | ||
"go.dedis.ch/kyber/v3/util/random" | ||
) | ||
|
||
// 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why we don't enforce this at the beginning of the function? Since the cost of the assertion is negligible, I'd add it. |
||
// | ||
// 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 0 ≤ j ≤ NQ-1, 0 ≤ i ≤ k-1 | ||
// | ||
// 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO it would be easier to use |
||
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-- { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove empty line. |
||
j := int(random.Int(big.NewInt(int64(i+1)), rand).Int64()) | ||
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++ { | ||
nkcr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why you use
kproof
? I thinkproof
is fine and later we can usep
instead ofproof
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as a non crypto guy, I highly appreciate when variables are not only single letters :D