diff --git a/rln/rln_test.go b/rln/rln_test.go index 5413524..15afc76 100644 --- a/rln/rln_test.go +++ b/rln/rln_test.go @@ -3,7 +3,6 @@ package rln import ( "bytes" "encoding/hex" - "fmt" "math" "testing" @@ -351,70 +350,155 @@ func (s *RLNSuite) TestGetMerkleProof() { } } -func (s *RLNSuite) TestGenerateRLNProofWithWitness() { +func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesOK() { + treeSize := 20 + rln, err := NewRLN() s.NoError(err) - // TODO: Run multiple test with multiple indexes - - // Leaf we generate the proof for - memberIndex := uint(4) - memKeys, err := rln.MembershipKeyGen() - s.NoError(err) + treeElements := make([]IdentityCredential, 0) // Create a Merkle tree with random members - for i := 0; i < 16; i++ { - if i == int(memberIndex) { - err := rln.InsertMember(memKeys.IDCommitment) - s.NoError(err) - } else { - memberKeys, err := rln.MembershipKeyGen() - s.NoError(err) + for i := 0; i < treeSize; i++ { + memberKeys, err := rln.MembershipKeyGen() + s.NoError(err) - err = rln.InsertMember(memberKeys.IDCommitment) - s.NoError(err) - } + err = rln.InsertMember(memberKeys.IDCommitment) + s.NoError(err) + treeElements = append(treeElements, *memberKeys) } - root, err := rln.GetMerkleRoot() + // We generate proofs with a custom witness aquired outside zerokit for diferent indexes + for _, memberIndex := range []uint{0, 10, 13, 15} { + root, err := rln.GetMerkleRoot() + s.NoError(err) + + // We provide out custom witness + merkleProof, err := rln.GetMerkleProof(memberIndex) + s.NoError(err) + + message := []byte("some rln protected message") + epoch := ToEpoch(1000) + + rlnWitness := CreateWitness( + treeElements[memberIndex].IDSecretHash, + message, + epoch, + merkleProof) + + // Generate a proof with our custom witness (Merkle Path of the memberIndex) + proofRes1, err := rln.GenerateRLNProofWithWitness(rlnWitness) + s.NoError(err) + verified1, err := rln.Verify(message, *proofRes1, root) + s.NoError(err) + s.True(verified1) + + // Generate a proof without our custom witness, to ensure they match + proofRes2, err := rln.GenerateProof(message, treeElements[memberIndex], MembershipIndex(memberIndex), epoch) + s.NoError(err) + + // Ensure we have the same root + s.Equal(root, proofRes1.MerkleRoot) + + // Proof generate with custom witness match the proof generate with the witness + // from zerokit. Proof itself is not asserted, can be different. + s.Equal(proofRes1.MerkleRoot, proofRes2.MerkleRoot) + s.Equal(proofRes1.Epoch, proofRes2.Epoch) + s.Equal(proofRes1.ShareX, proofRes2.ShareX) + s.Equal(proofRes1.ShareY, proofRes2.ShareY) + s.Equal(proofRes1.Nullifier, proofRes2.Nullifier) + s.Equal(proofRes1.RLNIdentifier, proofRes2.RLNIdentifier) + } +} + +func (s *RLNSuite) TestGenerateRLNProofWithWitness_VerifiesNOK() { + treeSize := 20 + + rln, err := NewRLN() s.NoError(err) - // TODO: Add some asserts on roots - fmt.Println("root from zerokit: ", root) + treeElements := make([]IdentityCredential, 0) - // We provide out custom witness - merkleProof, err := rln.GetMerkleProof(memberIndex) - s.NoError(err) + // Create a Merkle tree with random members + for i := 0; i < treeSize; i++ { + memberKeys, err := rln.MembershipKeyGen() + s.NoError(err) - message := []byte("some rln protected message") - epoch := ToEpoch(1000) - rlnWitness := CreateWitness( - *memKeys, - message, - epoch, - merkleProof) + err = rln.InsertMember(memberKeys.IDCommitment) + s.NoError(err) + treeElements = append(treeElements, *memberKeys) + } - // Generate a proof with our custom witness (Merkle Path of the memberIndex) - proofRes1, err := rln.GenerateRLNProofWithWitness(rlnWitness) - s.NoError(err) - verified1, err := rln.Verify(message, *proofRes1, root) - s.NoError(err) - s.True(verified1) + // We generate proofs with a custom witness aquired outside zerokit for diferent indexes + for _, memberIndex := range []uint{0, 10, 13, 15} { + root, err := rln.GetMerkleRoot() + s.NoError(err) - proofRes2, err := rln.GenerateProof(message, *memKeys, MembershipIndex(memberIndex), epoch) - s.NoError(err) + // We provide out custom witness + merkleProof, err := rln.GetMerkleProof(memberIndex) + s.NoError(err) - // Proof generate with custom witness match the proof generate with the witness - // from zerokit. Proof itself is not asserted, can be different. - s.Equal(proofRes1.MerkleRoot, proofRes2.MerkleRoot) - s.Equal(proofRes1.Epoch, proofRes2.Epoch) - s.Equal(proofRes1.ShareX, proofRes2.ShareX) - s.Equal(proofRes1.ShareY, proofRes2.ShareY) - s.Equal(proofRes1.Nullifier, proofRes2.Nullifier) - s.Equal(proofRes1.RLNIdentifier, proofRes2.RLNIdentifier) + message := []byte("some rln protected message") + epoch := ToEpoch(1000) - // TODO: test a proof that shall not be verified - // TODO: generate multiple proofs with random data + rlnWitness1 := CreateWitness( + treeElements[memberIndex].IDSecretHash, + message, + epoch, + merkleProof) + + // Generate a proof with our custom witness (Merkle Path of the memberIndex) + proofRes1, err := rln.GenerateRLNProofWithWitness(rlnWitness1) + s.NoError(err) + + // 1) Message changed, does not verify + verified1, err := rln.Verify([]byte("different message"), *proofRes1, root) + s.NoError(err) + s.False(verified1) + + // 2) Different epoch, does not verify + proofRes1.Epoch = ToEpoch(999) + verified2, err := rln.Verify(message, *proofRes1, root) + s.NoError(err) + s.False(verified2) + + // 3) Merkle proof in provided witness is wrong, does not verify + merkleProof.PathElements[0] = [32]byte{0x11} + rlnWitness2 := CreateWitness( + treeElements[memberIndex].IDSecretHash, + message, + epoch, + merkleProof) + + proofRes3, err := rln.GenerateRLNProofWithWitness(rlnWitness2) + s.NoError(err) + + verified3, err := rln.Verify(message, *proofRes3, root) + s.NoError(err) + s.False(verified3) + + // 4) Membership does not match the index (and nor part of tree), does not verify + merkleProof4, err := rln.GetMerkleProof(memberIndex) + s.NoError(err) + + // Membership that does not match the index + memberKeys, err := rln.MembershipKeyGen() + s.NoError(err) + + // Proof proves memberIndex inclusion, but provided membership is different + rlnWitness4 := CreateWitness( + memberKeys.IDSecretHash, + []byte("some rln protected message"), + ToEpoch(999), + merkleProof4) + + proofRes4, err := rln.GenerateRLNProofWithWitness(rlnWitness4) + s.NoError(err) + + verified4, err := rln.Verify(message, *proofRes4, root) + s.NoError(err) + s.False(verified4) + } } func (s *RLNSuite) TestEpochConsistency() { diff --git a/rln/serialize.go b/rln/serialize.go index aad97d3..b10664b 100644 --- a/rln/serialize.go +++ b/rln/serialize.go @@ -51,7 +51,7 @@ func (r RateLimitProof) serialize() []byte { func (r *RLNWitnessInput) serialize() []byte { output := make([]byte, 0) - output = append(output, r.IdentityCredential.IDSecretHash[:]...) + output = append(output, r.IDSecretHash[:]...) output = append(output, r.MerkleProof.serialize()...) output = append(output, r.X[:]...) output = append(output, r.Epoch[:]...) diff --git a/rln/serialize_test.go b/rln/serialize_test.go index f810cdc..9bc8f2b 100644 --- a/rln/serialize_test.go +++ b/rln/serialize_test.go @@ -60,9 +60,7 @@ func TestRLNWitnessInputSerDe(t *testing.T) { } witness := RLNWitnessInput{ - IdentityCredential: IdentityCredential{ - IDSecretHash: random32(), - }, + IDSecretHash: random32(), MerkleProof: mProof, X: [32]byte{0x00}, Epoch: ToEpoch(10), diff --git a/rln/types.go b/rln/types.go index 3d4b736..7d8f0dc 100644 --- a/rln/types.go +++ b/rln/types.go @@ -70,14 +70,11 @@ type MerkleProof struct { // Equivalent: https://github.com/vacp2p/zerokit/blob/v0.3.5/rln/src/protocol.rs#L33-L40 type RLNWitnessInput struct { - // TODO: Maybe dont store the whole IdentityCredential and just the secret - IdentityCredential IdentityCredential `json:"identityCredential"` - MerkleProof MerkleProof `json:"merkleProof"` - - // This is not the data but the hashed version of it "x"..TODO rename and reconsider + IDSecretHash IDSecretHash `json:"identitySecretHash"` + MerkleProof MerkleProof `json:"merkleProof"` X [32]byte `json:"x"` Epoch Epoch `json:"epoch"` - RlnIdentifier RLNIdentifier `json:"rlnIdentifier"` // what is this? TOOD: app specific. which one is ours? + RlnIdentifier RLNIdentifier `json:"rlnIdentifier"` } type TreeDepth int diff --git a/rln/utils.go b/rln/utils.go index ce5b05c..a43e2a3 100644 --- a/rln/utils.go +++ b/rln/utils.go @@ -10,17 +10,17 @@ import ( ) func CreateWitness( - identityCredential IdentityCredential, // TODO only the secret hash. + idSecretHash IDSecretHash, data []byte, epoch [32]byte, merkleProof MerkleProof) RLNWitnessInput { return RLNWitnessInput{ - IdentityCredential: identityCredential, - MerkleProof: merkleProof, - X: HashToBN255(data), - Epoch: epoch, - RlnIdentifier: RLN_IDENTIFIER, + IDSecretHash: idSecretHash, + MerkleProof: merkleProof, + X: HashToBN255(data), + Epoch: epoch, + RlnIdentifier: RLN_IDENTIFIER, } }