From 74af495f04611e88521b5783dc8f6f9280403d5b Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Wed, 9 Oct 2024 17:07:27 -0700 Subject: [PATCH 01/30] - equality proofs + tests --- pkg/encryption/elgamal/common.go | 7 + pkg/encryption/elgamal/encryption_test.go | 34 +-- pkg/encryption/elgamal/types_test.go | 2 +- .../ciphertext_ciphertext_equality.go | 258 ++++++++++++++++++ .../ciphertext_ciphertext_equality_test.go | 242 ++++++++++++++++ pkg/zkproofs/equality_proof_transcript.go | 35 +++ 6 files changed, 557 insertions(+), 21 deletions(-) create mode 100644 pkg/zkproofs/ciphertext_ciphertext_equality.go create mode 100644 pkg/zkproofs/ciphertext_ciphertext_equality_test.go create mode 100644 pkg/zkproofs/equality_proof_transcript.go diff --git a/pkg/encryption/elgamal/common.go b/pkg/encryption/elgamal/common.go index fe5e42a..0f6ca5b 100644 --- a/pkg/encryption/elgamal/common.go +++ b/pkg/encryption/elgamal/common.go @@ -2,7 +2,9 @@ package elgamal import ( "crypto/ecdsa" + "crypto/rand" "crypto/sha256" + "github.com/ethereum/go-ethereum/crypto/secp256k1" "io" "github.com/coinbase/kryptology/pkg/core/curves" @@ -12,6 +14,11 @@ import ( // H_STRING H is a random point on the elliptic curve that is unrelated to G. const H_STRING = "gPt25pi0eDphSiXWu0BIeIvyVATCtwhslTqfqvNhW2c" +// GenerateKey generates a new ECDSA key pair. +func GenerateKey() (*ecdsa.PrivateKey, error) { + return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader) +} + // KeyGen generates a new key pair for the Twisted ElGamal encryption scheme. func (teg TwistedElGamal) KeyGen(privateKey ecdsa.PrivateKey, denom string) (*KeyPair, error) { // Fixed base point H diff --git a/pkg/encryption/elgamal/encryption_test.go b/pkg/encryption/elgamal/encryption_test.go index 57e15fb..7660f59 100644 --- a/pkg/encryption/elgamal/encryption_test.go +++ b/pkg/encryption/elgamal/encryption_test.go @@ -1,10 +1,8 @@ package elgamal import ( - "crypto/ecdsa" "crypto/rand" "github.com/coinbase/kryptology/pkg/core/curves" - "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/stretchr/testify/require" "math" "testing" @@ -12,12 +10,8 @@ import ( const DefaultTestDenom = "factory/sei1239081236472sd/testToken" -func generateKey() (*ecdsa.PrivateKey, error) { - return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader) -} - func TestKeyGeneration(t *testing.T) { - privateKey, err := generateKey() + privateKey, err := GenerateKey() require.Nil(t, err) eg := NewTwistedElgamal() @@ -42,7 +36,7 @@ func TestKeyGeneration(t *testing.T) { require.NotEqual(t, keyPair, keyPairDiffSalt, "PK should be different for different salt") // Test that different privateKey should generate different PK - altPrivateKey, err := generateKey() + altPrivateKey, err := GenerateKey() require.Nil(t, err) keyPairDiffPK, err := eg.KeyGen(*altPrivateKey, altDenom) require.Nil(t, err) @@ -50,8 +44,8 @@ func TestKeyGeneration(t *testing.T) { } func TestEncryptionDecryption(t *testing.T) { - privateKey, _ := generateKey() - altPrivateKey, _ := generateKey() + privateKey, _ := GenerateKey() + altPrivateKey, _ := GenerateKey() eg := NewTwistedElgamal() @@ -86,7 +80,7 @@ func TestEncryptionDecryption(t *testing.T) { // Due to the size of 48 bit numbers, this test takes a really long time (~1hr) to run. func Test48BitEncryptionDecryption(t *testing.T) { - privateKey, err := generateKey() + privateKey, err := GenerateKey() require.Nil(t, err) eg := NewTwistedElgamal() @@ -127,8 +121,8 @@ func Test48BitEncryptionDecryption(t *testing.T) { } func TestAddCiphertext(t *testing.T) { - privateKey, _ := generateKey() - altPrivateKey, _ := generateKey() + privateKey, _ := GenerateKey() + altPrivateKey, _ := GenerateKey() eg := NewTwistedElgamal() @@ -175,7 +169,7 @@ func TestAddCiphertext(t *testing.T) { func TestTwistedElGamal_InvalidCiphertext(t *testing.T) { eg := NewTwistedElgamal() - privateKey, _ := generateKey() + privateKey, _ := GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) invalidCt := &Ciphertext{} @@ -190,7 +184,7 @@ func TestTwistedElGamal_NilPrivateKey(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := generateKey() + privateKey, _ := GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) // Encrypt a value with a valid public key @@ -209,7 +203,7 @@ func TestTwistedElGamal_EncryptDecryptWithRand(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := generateKey() + privateKey, _ := GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) message := uint64(555555555) @@ -227,7 +221,7 @@ func TestTwistedElGamal_EncryptMessageTwice(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := generateKey() + privateKey, _ := GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) message := uint64(555555555) @@ -243,7 +237,7 @@ func TestTwistedElGamal_DecryptWithZeroBits(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := generateKey() + privateKey, _ := GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) message := uint64(555555555) @@ -269,7 +263,7 @@ func TestTwistedElGamal_EncryptInvalidRandomFactor(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := generateKey() + privateKey, _ := GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) // Test with nil public key @@ -282,7 +276,7 @@ func TestTwistedElGamal_EncryptBoundaryValues(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := generateKey() + privateKey, _ := GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) // Test with the smallest possible value (0) diff --git a/pkg/encryption/elgamal/types_test.go b/pkg/encryption/elgamal/types_test.go index a419803..5ad76f0 100644 --- a/pkg/encryption/elgamal/types_test.go +++ b/pkg/encryption/elgamal/types_test.go @@ -7,7 +7,7 @@ import ( ) func TestCiphertext_MarshalJSON(t *testing.T) { - privateKey, _ := generateKey() + privateKey, _ := GenerateKey() eg := NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality.go b/pkg/zkproofs/ciphertext_ciphertext_equality.go new file mode 100644 index 0000000..a20b369 --- /dev/null +++ b/pkg/zkproofs/ciphertext_ciphertext_equality.go @@ -0,0 +1,258 @@ +package zkproofs + +import ( + "crypto/rand" + "encoding/json" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" +) + +// CiphertextCiphertextEqualityProof represents a zero-knowledge proof that two ciphertexts are encrypting the same +// value. +type CiphertextCiphertextEqualityProof struct { + Y0 curves.Point + Y1 curves.Point + Y2 curves.Point + Y3 curves.Point + Zs curves.Scalar + Zx curves.Scalar + Zr curves.Scalar +} + +// NewCiphertextCiphertextEqualityProof generates a new ciphertext-ciphertext proof. +// This proof demonstrates that two ciphertexts are encrypting the same value. +// +// Parameters: +// - sourceKeypair: The ElGamal keypair associated with the first ciphertext to be proved. +// - destinationPubkey: The ElGamal pubkey associated with the second ElGamal ciphertext. +// - sourceCiphertext: The first ElGamal ciphertext for which the prover knows a decryption key. +// - destinationOpening: The opening (randomness) associated with the second ElGamal ciphertext. +// - amount: The message associated with the ElGamal ciphertext and Pedersen commitment. +func NewCiphertextCiphertextEqualityProof( + sourceKeypair *elgamal.KeyPair, + destinationPubkey *curves.Point, + sourceCiphertext *elgamal.Ciphertext, + destinationOpening *curves.Scalar, + amount *curves.Scalar, +) (*CiphertextCiphertextEqualityProof, error) { + // Extract necessary values + pSource := sourceKeypair.PublicKey + dSource := sourceCiphertext.D + pDestination := *destinationPubkey + + s := sourceKeypair.PrivateKey + x := *amount + r := *destinationOpening + + // Generate random scalars + ed25519 := curves.ED25519() + ys := ed25519.Scalar.Random(rand.Reader) + yx := ed25519.Scalar.Random(rand.Reader) + yr := ed25519.Scalar.Random(rand.Reader) + + eg := elgamal.NewTwistedElgamal() + G := eg.GetG() + H := eg.GetH() + + // Compute Y0, Y1, Y2, Y3 + // Compute Y0 = ys * pSource + Y0 := pSource.Mul(ys) + + // Compute Y1 = yx * G + ys * dSource + yxG := G.Mul(yx) + ysD := dSource.Mul(ys) + Y1 := yxG.Add(ysD) + + // Compute Y2 = yx * G + yr * H + yrH := H.Mul(yr) + Y2 := yxG.Add(yrH) + + // Compute Y3 = yr * pDestination + Y3 := pDestination.Mul(yr) + + // Append to transcript + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("Y0", Y0.ToAffineCompressed()) + transcript.AppendMessage("Y1", Y1.ToAffineCompressed()) + transcript.AppendMessage("Y2", Y2.ToAffineCompressed()) + transcript.AppendMessage("Y3", Y3.ToAffineCompressed()) + + // Generate challenge scalar + c := transcript.ChallengeScalar() + + // Compute masked values + Zs := ys.Add(c.Mul(s)) + Zx := yx.Add(c.Mul(x)) + Zr := yr.Add(c.Mul(r)) + + // Return proof + return &CiphertextCiphertextEqualityProof{ + Y0: Y0, + Y1: Y1, + Y2: Y2, + Y3: Y3, + Zs: Zs, + Zx: Zx, + Zr: Zr, + }, nil +} + +// Verify Function to verify the cipher-cipher proof +// Parameters: +// - proof: The proof to be verified. +// - sourcePubKey: The ElGamal public key associated with the first ElGamal ciphertext. +// - destinationPubKey: The ElGamal public key associated with the second ElGamal ciphertext. +// - sourceCiphertext: The first ElGamal ciphertext to be compared. +// - destinationCiphertext: The second ElGamal ciphertext to be compared. +func Verify( + proof *CiphertextCiphertextEqualityProof, + sourcePubKey *curves.Point, + destinationPubKey *curves.Point, + sourceCiphertext *elgamal.Ciphertext, + destinationCiphertext *elgamal.Ciphertext, +) bool { + // Extract necessary values + pSource := *sourcePubKey + cSource := sourceCiphertext.C + dSource := sourceCiphertext.D + + pDestination := *destinationPubKey + cDestination := destinationCiphertext.C + dDestination := destinationCiphertext.D + + // Recreate the transcript + transcript := NewEqualityProofTranscript() + // Append Y0, Y1, Y2, Y3 to transcript + transcript.AppendMessage("Y0", proof.Y0.ToAffineCompressed()) + transcript.AppendMessage("Y1", proof.Y1.ToAffineCompressed()) + transcript.AppendMessage("Y2", proof.Y2.ToAffineCompressed()) + transcript.AppendMessage("Y3", proof.Y3.ToAffineCompressed()) + + // Generate challenge scalar + c := transcript.ChallengeScalar() + + eg := elgamal.NewTwistedElgamal() + + // Extract G and H base points + G := eg.GetG() + H := eg.GetH() + + // Check Y0: zs * P_source == c * H + Y0 + lhsY0 := pSource.Mul(proof.Zs) + cH := H.Mul(c) + rhsY0 := proof.Y0.Add(cH) + + if !lhsY0.Equal(rhsY0) { + return false + } + + // Check zx * G + zs * D_source == c * C_source + Y1 + zxG := G.Mul(proof.Zx) + zsD := dSource.Mul(proof.Zs) + lhsY1 := zxG.Add(zsD) + + cC := cSource.Mul(c) + rhsY1 := proof.Y1.Add(cC) + + if !lhsY1.Equal(rhsY1) { + return false + } + + // Check Y2: zx * G + zr * H == c * C_destination + Y2 + zrH := H.Mul(proof.Zr) + lhsY2 := zxG.Add(zrH) + + cCd := cDestination.Mul(c) + rhsY2 := proof.Y2.Add(cCd) + + if !lhsY2.Equal(rhsY2) { + return false + } + + // Check Y3: zr * P_destination == c * D_destination + Y3 + lhsY3 := pDestination.Mul(proof.Zr) + cDd := dDestination.Mul(c) + + rhsY3 := proof.Y3.Add(cDd) + + if !lhsY3.Equal(rhsY3) { + return false + } + + return true +} + +// MarshalJSON for CiphertextCiphertextEqualityProof +func (p *CiphertextCiphertextEqualityProof) MarshalJSON() ([]byte, error) { + // Serialize the points and scalars to a format you prefer + return json.Marshal(map[string]interface{}{ + "y0": p.Y0.ToAffineCompressed(), // Assuming ToAffineCompressed returns a byte slice + "y1": p.Y1.ToAffineCompressed(), // Serialize Point to compressed bytes + "y2": p.Y2.ToAffineCompressed(), // Serialize Point to compressed bytes + "y3": p.Y3.ToAffineCompressed(), // Serialize Point to compressed bytes + "zs": p.Zs.Bytes(), // Serialize Scalar to bytes + "zx": p.Zx.Bytes(), // Serialize Scalar to bytes + "zr": p.Zr.Bytes(), // Serialize Scalar to bytes + }) +} + +// UnmarshalJSON for CiphertextCiphertextEqualityProof +func (p *CiphertextCiphertextEqualityProof) UnmarshalJSON(data []byte) error { + // Create a temporary structure to decode JSON + var temp struct { + Y0 []byte `json:"y0"` + Y1 []byte `json:"y1"` + Y2 []byte `json:"y2"` + Y3 []byte `json:"y3"` + Zs []byte `json:"zs"` + Zx []byte `json:"zx"` + Zr []byte `json:"zr"` + } + + // Unmarshal into the temp structure + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + ed25519Curve := curves.ED25519() + // Convert the byte arrays back into curve points and scalars + y0, err := ed25519Curve.Point.FromAffineCompressed(temp.Y0) + if err != nil { + return err + } + y1, err := ed25519Curve.Point.FromAffineCompressed(temp.Y1) + if err != nil { + return err + } + y2, err := ed25519Curve.Point.FromAffineCompressed(temp.Y2) + if err != nil { + return err + } + y3, err := ed25519Curve.Point.FromAffineCompressed(temp.Y3) + if err != nil { + return err + } + zs, err := ed25519Curve.Scalar.SetBytes(temp.Zs) + if err != nil { + return err + } + zx, err := ed25519Curve.Scalar.SetBytes(temp.Zx) + if err != nil { + return err + } + zr, err := ed25519Curve.Scalar.SetBytes(temp.Zr) + if err != nil { + return err + } + + // Assign the decoded values to the CiphertextCiphertextEqualityProof struct + p.Y0 = y0 + p.Y1 = y1 + p.Y2 = y2 + p.Y3 = y3 + p.Zs = zs + p.Zx = zx + p.Zr = zr + + return nil +} diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go new file mode 100644 index 0000000..1ef8d28 --- /dev/null +++ b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go @@ -0,0 +1,242 @@ +package zkproofs + +import ( + "encoding/json" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "math/big" + "testing" +) + +const TestDenom = "factory/sei15zv4wz8kpa4jz5ahretje97u5xsp9vttvyaffd/testToken" + +func TestCiphertextCiphertextEqualityProof(t *testing.T) { + tests := []struct { + name string + sourceAmount uint64 + destinationAmount uint64 + useDifferentPublicKey bool + expectValid bool + }{ + { + name: "Valid Proof - Equal Amounts", + sourceAmount: 100, + destinationAmount: 100, + useDifferentPublicKey: false, + expectValid: true, + }, + { + name: "Invalid Proof - Mismatched Amounts", + sourceAmount: 100, + destinationAmount: 101, + useDifferentPublicKey: false, + expectValid: false, + }, + { + name: "Invalid Proof - Different Public Keys", + sourceAmount: 200, + destinationAmount: 200, + useDifferentPublicKey: true, + expectValid: false, + }, + { + name: "Invalid Proof - Different Public Keys and Mismatched Amounts", + sourceAmount: 150, + destinationAmount: 151, + useDifferentPublicKey: true, + expectValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Key generation + sourcePrivateKey, err := elgamal.GenerateKey() + destPrivateKey, err := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) + destinationKeypair, err := eg.KeyGen(*destPrivateKey, TestDenom) + + var actualDestinationPubkey *curves.Point + if tt.useDifferentPublicKey { + altDestPrivateKey, _ := elgamal.GenerateKey() + // Generate an alternative keypair for destination + altDestinationKeypair, _ := eg.KeyGen(*altDestPrivateKey, TestDenom) + actualDestinationPubkey = &altDestinationKeypair.PublicKey + } else { + actualDestinationPubkey = &destinationKeypair.PublicKey + } + + // Encrypt the source amount + sourceCiphertext, _, err := eg.Encrypt(sourceKeypair.PublicKey, tt.sourceAmount) + assert.NoError(t, err, "Encryption should succeed for sourceCiphertext") + + // Encrypt the destination amount + destinationCiphertext, destinationRandomness, err := eg.Encrypt(destinationKeypair.PublicKey, tt.destinationAmount) + assert.NoError(t, err, "Encryption should succeed for destinationCiphertext") + + srcAmtValue := new(big.Int).SetUint64(tt.sourceAmount) + amount, _ := curves.ED25519().Scalar.SetBigInt(srcAmtValue) + + // Generate the proof + proof, err := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + actualDestinationPubkey, + sourceCiphertext, + &destinationRandomness, + &amount, + ) + assert.NoError(t, err, "Proof generation should not fail") + + // Verify the proof + valid := Verify( + proof, + &sourceKeypair.PublicKey, + actualDestinationPubkey, + sourceCiphertext, + destinationCiphertext, + ) + + if tt.expectValid { + assert.True(t, valid, "Proof verification should succeed") + } else { + assert.False(t, valid, "Proof verification should fail") + } + }) + } +} + +func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { + t.Run("Zero Amounts", func(t *testing.T) { + // Key generation + sourcePrivateKey, err := elgamal.GenerateKey() + destPrivateKey, err := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) + destinationKeypair, err := eg.KeyGen(*destPrivateKey, TestDenom) + + amount := uint64(0) + + // Encrypt the amount using source and destination public keys + sourceCiphertext, _, err := eg.Encrypt(sourceKeypair.PublicKey, amount) + assert.NoError(t, err, "Encryption should succeed for sourceCiphertext") + + destinationCiphertext, destinationRandomness, err := eg.Encrypt(destinationKeypair.PublicKey, amount) + assert.NoError(t, err, "Encryption should succeed for destinationCiphertext") + + srcAmtValue := new(big.Int).SetUint64(amount) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(srcAmtValue) + + // Generate the proof + proof, err := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + &destinationKeypair.PublicKey, + sourceCiphertext, + &destinationRandomness, + &scalarAmount, + ) + assert.NoError(t, err, "Proof generation should succeed for zero amounts") + + // Verify the proof + valid := Verify( + proof, + &sourceKeypair.PublicKey, + &destinationKeypair.PublicKey, + sourceCiphertext, + destinationCiphertext, + ) + assert.True(t, valid, "Proof verification should succeed for zero amounts") + }) + + t.Run("Maximum Amount", func(t *testing.T) { + // Key generation + sourcePrivateKey, err := elgamal.GenerateKey() + destPrivateKey, err := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) + + destinationKeypair, err := eg.KeyGen(*destPrivateKey, TestDenom) + + amount := uint64(1 << 60) // A large amount to test scalability + + // Encrypt the amount using source and destination public keys + sourceCiphertext, _, err := eg.Encrypt(sourceKeypair.PublicKey, amount) + assert.NoError(t, err, "Encryption should succeed for sourceCiphertext") + + destinationCiphertext, destinationRandomness, err := eg.Encrypt(destinationKeypair.PublicKey, amount) + assert.NoError(t, err, "Encryption should succeed for destinationCiphertext") + + // Generate the proof + scalarAmtValue := new(big.Int).SetUint64(amount) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) + + // Generate the proof + proof, err := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + &destinationKeypair.PublicKey, + sourceCiphertext, + &destinationRandomness, + &scalarAmount, + ) + assert.NoError(t, err, "Proof generation should succeed for maximum amounts") + + // Verify the proof + valid := Verify( + proof, + &sourceKeypair.PublicKey, + &destinationKeypair.PublicKey, + sourceCiphertext, + destinationCiphertext, + ) + assert.True(t, valid, "Proof verification should succeed for maximum amounts") + }) +} + +func TestCiphertextCiphertextEqualityProof_UnmarshalJSON_Valid(t *testing.T) { + sourcePrivateKey, err := elgamal.GenerateKey() + destPrivateKey, err := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) + destinationKeypair, err := eg.KeyGen(*destPrivateKey, TestDenom) + + amount := uint64(100) + + // Encrypt the amount using source and destination public keys + sourceCiphertext, _, err := eg.Encrypt(sourceKeypair.PublicKey, amount) + _, destinationRandomness, err := eg.Encrypt(destinationKeypair.PublicKey, amount) + scalarAmtValue := new(big.Int).SetUint64(amount) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) + + proof, err := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + &destinationKeypair.PublicKey, + sourceCiphertext, + &destinationRandomness, + &scalarAmount, + ) + require.NoError(t, err, "Proof generation should not fail") + + // Marshal the proof to JSON + data, err := proof.MarshalJSON() + require.NoError(t, err, "Marshaling should not fail") + + // Unmarshal the JSON back to a proof + var unmarshaled CiphertextCiphertextEqualityProof + err = json.Unmarshal(data, &unmarshaled) + require.NoError(t, err, "Unmarshaling should not fail") + + // Type assert to CiphertextCiphertextEqualityProof + //unmarshaledProof, ok := unmarshaled.(*CiphertextCiphertextEqualityProof) + //require.True(t, ok, "Unmarshaled proof should be of type CiphertextCiphertextEqualityProof") + + // Compare original and unmarshaled proofs + require.True(t, proof.Y0.Equal(unmarshaled.Y0), "Y0 points should be equal after unmarshaling") + require.True(t, proof.Y1.Equal(unmarshaled.Y1), "Y1 points should be equal after unmarshaling") + require.True(t, proof.Y2.Equal(unmarshaled.Y2), "Y2 points should be equal after unmarshaling") + require.True(t, proof.Y3.Equal(unmarshaled.Y3), "Y3 points should be equal after unmarshaling") + require.Equal(t, proof.Zs, unmarshaled.Zs, "Zs scalars should be equal after unmarshaling") + require.Equal(t, proof.Zx, unmarshaled.Zx, "Zx scalars should be equal after unmarshaling") + require.Equal(t, proof.Zr, unmarshaled.Zr, "Zr scalars should be equal after unmarshaling") +} diff --git a/pkg/zkproofs/equality_proof_transcript.go b/pkg/zkproofs/equality_proof_transcript.go new file mode 100644 index 0000000..2b17c7f --- /dev/null +++ b/pkg/zkproofs/equality_proof_transcript.go @@ -0,0 +1,35 @@ +package zkproofs + +import ( + "crypto/sha512" + "fmt" + "github.com/coinbase/kryptology/pkg/core/curves" +) + +type EqualityProofTranscript struct { + messages [][]byte +} + +func NewEqualityProofTranscript() *EqualityProofTranscript { + return &EqualityProofTranscript{messages: make([][]byte, 0)} +} + +func (t *EqualityProofTranscript) AppendMessage(label string, data []byte) { + t.messages = append(t.messages, append([]byte(label), data...)) +} + +func (t *EqualityProofTranscript) ChallengeScalar() curves.Scalar { + hasher := sha512.New() + for _, msg := range t.messages { + hasher.Write(msg) + } + var sum [64]byte + copy(sum[:], hasher.Sum(nil)) + + s, err := curves.ED25519().Scalar.SetBytesWide(sum[:]) + if err != nil { + fmt.Println(err) + panic(err) + } + return s +} From 67f138d478889cbaa20e49bae01251cdb8c049a2 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 10 Oct 2024 16:07:13 -0700 Subject: [PATCH 02/30] - transcript tests --- .../equality_proof_transcript_test.go | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 pkg/zkproofs/equality_proof_transcript_test.go diff --git a/pkg/zkproofs/equality_proof_transcript_test.go b/pkg/zkproofs/equality_proof_transcript_test.go new file mode 100644 index 0000000..6e86870 --- /dev/null +++ b/pkg/zkproofs/equality_proof_transcript_test.go @@ -0,0 +1,46 @@ +package zkproofs + +import ( + "crypto/sha512" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestEqualityProofTranscript_AppendMessage(t *testing.T) { + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("label1", []byte("data1")) + transcript.AppendMessage("label2", []byte("data2")) + + assert.Equal(t, 2, len(transcript.messages), "Number of messages should be 2") + assert.Equal(t, "label1data1", string(transcript.messages[0]), "First message should match") + assert.Equal(t, "label2data2", string(transcript.messages[1]), "Second message should match") +} + +func TestEqualityProofTranscript_ChallengeScalar(t *testing.T) { + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("label1", []byte("data1")) + transcript.AppendMessage("label2", []byte("data2")) + + scalar := transcript.ChallengeScalar() + hasher := sha512.New() + hasher.Write([]byte("label1data1")) + hasher.Write([]byte("label2data2")) + var sum [64]byte + copy(sum[:], hasher.Sum(nil)) + expectedScalar, _ := curves.ED25519().Scalar.SetBytesWide(sum[:]) + + assert.Equal(t, expectedScalar, scalar, "Challenge scalar should match expected value") +} + +func TestEqualityProofTranscript_EmptyMessages(t *testing.T) { + transcript := NewEqualityProofTranscript() + scalar := transcript.ChallengeScalar() + + hasher := sha512.New() + var sum [64]byte + copy(sum[:], hasher.Sum(nil)) + expectedScalar, _ := curves.ED25519().Scalar.SetBytesWide(sum[:]) + + assert.Equal(t, expectedScalar, scalar, "Challenge scalar should match expected value for empty messages") +} From 6fe10205e62ff9cad8e952e8b5c03105f12d0f55 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 10 Oct 2024 16:08:13 -0700 Subject: [PATCH 03/30] - transcript docs --- pkg/zkproofs/equality_proof_transcript.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/zkproofs/equality_proof_transcript.go b/pkg/zkproofs/equality_proof_transcript.go index 2b17c7f..22cb955 100644 --- a/pkg/zkproofs/equality_proof_transcript.go +++ b/pkg/zkproofs/equality_proof_transcript.go @@ -14,10 +14,12 @@ func NewEqualityProofTranscript() *EqualityProofTranscript { return &EqualityProofTranscript{messages: make([][]byte, 0)} } +// AppendMessage appends a message to the transcript func (t *EqualityProofTranscript) AppendMessage(label string, data []byte) { t.messages = append(t.messages, append([]byte(label), data...)) } +// ChallengeScalar generates a challenge scalar from the transcript func (t *EqualityProofTranscript) ChallengeScalar() curves.Scalar { hasher := sha512.New() for _, msg := range t.messages { From 87f5d6a8c4851cb9721e5f6a7f38d77ec68d804b Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 10 Oct 2024 16:22:13 -0700 Subject: [PATCH 04/30] - ciphertext commitment proofs + tests --- .../ciphertext_ciphertext_equality.go | 4 +- .../ciphertext_ciphertext_equality_test.go | 12 +- .../ciphertext_commitment_equality.go | 209 ++++++++++++++++++ .../ciphertext_commitment_equality_test.go | 159 +++++++++++++ 4 files changed, 376 insertions(+), 8 deletions(-) create mode 100644 pkg/zkproofs/ciphertext_commitment_equality.go create mode 100644 pkg/zkproofs/ciphertext_commitment_equality_test.go diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality.go b/pkg/zkproofs/ciphertext_ciphertext_equality.go index a20b369..56057e5 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality.go @@ -97,14 +97,14 @@ func NewCiphertextCiphertextEqualityProof( }, nil } -// Verify Function to verify the cipher-cipher proof +// VerifyCiphertextCiphertextEquality Function to verify the cipher-cipher proof // Parameters: // - proof: The proof to be verified. // - sourcePubKey: The ElGamal public key associated with the first ElGamal ciphertext. // - destinationPubKey: The ElGamal public key associated with the second ElGamal ciphertext. // - sourceCiphertext: The first ElGamal ciphertext to be compared. // - destinationCiphertext: The second ElGamal ciphertext to be compared. -func Verify( +func VerifyCiphertextCiphertextEquality( proof *CiphertextCiphertextEqualityProof, sourcePubKey *curves.Point, destinationPubKey *curves.Point, diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go index 1ef8d28..b67d83c 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go @@ -90,8 +90,8 @@ func TestCiphertextCiphertextEqualityProof(t *testing.T) { ) assert.NoError(t, err, "Proof generation should not fail") - // Verify the proof - valid := Verify( + // VerifyCiphertextCiphertextEquality the proof + valid := VerifyCiphertextCiphertextEquality( proof, &sourceKeypair.PublicKey, actualDestinationPubkey, @@ -139,8 +139,8 @@ func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { ) assert.NoError(t, err, "Proof generation should succeed for zero amounts") - // Verify the proof - valid := Verify( + // VerifyCiphertextCiphertextEquality the proof + valid := VerifyCiphertextCiphertextEquality( proof, &sourceKeypair.PublicKey, &destinationKeypair.PublicKey, @@ -182,8 +182,8 @@ func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { ) assert.NoError(t, err, "Proof generation should succeed for maximum amounts") - // Verify the proof - valid := Verify( + // VerifyCiphertextCiphertextEquality the proof + valid := VerifyCiphertextCiphertextEquality( proof, &sourceKeypair.PublicKey, &destinationKeypair.PublicKey, diff --git a/pkg/zkproofs/ciphertext_commitment_equality.go b/pkg/zkproofs/ciphertext_commitment_equality.go new file mode 100644 index 0000000..6e1cf19 --- /dev/null +++ b/pkg/zkproofs/ciphertext_commitment_equality.go @@ -0,0 +1,209 @@ +package zkproofs + +import ( + "crypto/rand" + "encoding/json" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" +) + +// CiphertextCommitmentEqualityProof represents a zero-knowledge proof that a ciphertext and a Pedersen commitment +// encode the same message. +type CiphertextCommitmentEqualityProof struct { + Y0 curves.Point + Y1 curves.Point + Y2 curves.Point + Zs curves.Scalar + Zx curves.Scalar + Zr curves.Scalar +} + +// NewCiphertextCommitmentEqualityProof generates a new equality proof between a ciphertext and a Pedersen commitment. +func NewCiphertextCommitmentEqualityProof( + sourceKeypair *elgamal.KeyPair, + sourceCiphertext *elgamal.Ciphertext, + pedersenOpening *curves.Scalar, + amount *curves.Scalar, +) (*CiphertextCommitmentEqualityProof, error) { + // Extract necessary values + P := sourceKeypair.PublicKey // Public key in twisted ElGamal aesgcm scheme + D := sourceCiphertext.D // D part of the twisted ElGamal ciphertext + + s := sourceKeypair.PrivateKey // Secret key in twisted ElGamal aesgcm scheme + x := amount // Message in twisted ElGamal aesgcm scheme + r := pedersenOpening // Pedersen commitment + eg := elgamal.NewTwistedElgamal() + G := eg.GetG() // Fixed base point G + H := eg.GetH() // Fixed base point H + + ed25519 := curves.ED25519() + // Generate random masking factors + ys := ed25519.Scalar.Random(rand.Reader) + yx := ed25519.Scalar.Random(rand.Reader) + yr := ed25519.Scalar.Random(rand.Reader) + + // Compute Y0 = ys * P + Y0 := P.Mul(ys) + + // Compute Y1 = yx * G + ys * D + yxG := G.Mul(yx) + ysD := D.Mul(ys) + Y1 := yxG.Add(ysD) + + // Compute Y2 = yx * G + yr * H + yrH := H.Mul(yr) + Y2 := yxG.Add(yrH) + + // Append commitments to the transcript + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("Y0", Y0.ToAffineCompressed()) + transcript.AppendMessage("Y1", Y1.ToAffineCompressed()) + transcript.AppendMessage("Y2", Y2.ToAffineCompressed()) + + // Generate the challenge scalar from the transcript + c := transcript.ChallengeScalar() + + // Compute responses + // zs = c * s + ys + Zs := c.Mul(s).Add(ys) + // zx = c * x + yx + Zx := c.Mul(*x).Add(yx) + // zr = c * r + yr + Zr := c.Mul(*r).Add(yr) + + // Return the proof + return &CiphertextCommitmentEqualityProof{ + Y0: Y0, + Y1: Y1, + Y2: Y2, + Zs: Zs, + Zx: Zx, + Zr: Zr, + }, nil +} + +// VerifyCiphertextCommitmentEquality verifies the zero-knowledge equality proof between a ciphertext and a Pedersen +// commitment. +func VerifyCiphertextCommitmentEquality( + proof *CiphertextCommitmentEqualityProof, + sourcePubKey *curves.Point, + sourceCiphertext *elgamal.Ciphertext, + pedersenCommitment *curves.Point, +) bool { + // Extract necessary values + P := *sourcePubKey + D := sourceCiphertext.D + cEg := sourceCiphertext.C + cPed := *pedersenCommitment + + eg := elgamal.NewTwistedElgamal() + G := eg.GetG() + H := eg.GetH() + + // Append commitments to the transcript + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("Y0", proof.Y0.ToAffineCompressed()) + transcript.AppendMessage("Y1", proof.Y1.ToAffineCompressed()) + transcript.AppendMessage("Y2", proof.Y2.ToAffineCompressed()) + + // Generate the challenge scalar from the transcript + c := transcript.ChallengeScalar() + + // VerifyCipherCipherEquality zs * P == c * H + Y0 + lhsY0 := P.Mul(proof.Zs) // zs * PEG + cH := H.Mul(c) // c * H + rhsY0 := cH.Add(proof.Y0) + if !lhsY0.Equal(rhsY0) { + return false + } + + // VerifyCipherCipherEquality zx * G + zs * D == c * cEg + Y1 + zxG := G.Mul(proof.Zx) // zx * G + zsD := D.Mul(proof.Zs) // zs * D + lhsY1 := zxG.Add(zsD) // zx * G + zs * D + cCeg := cEg.Mul(c) // c * cEg + rhsY1 := cCeg.Add(proof.Y1) + if !lhsY1.Equal(rhsY1) { + return false + } + + // VerifyCipherCipherEquality zx * G + zr * H == c * cPed + Y2 + zxG2 := G.Mul(proof.Zx) // zx * G + zrH := H.Mul(proof.Zr) // zr * H + lhsY2 := zxG2.Add(zrH) // zx * G + zr * H + cCPed := cPed.Mul(c) // c * cPed + rhsY2 := cCPed.Add(proof.Y2) // c * cPed + Y2 + if !lhsY2.Equal(rhsY2) { + return false + } + + return true +} + +// MarshalJSON for CiphertextCommitmentEqualityProof +func (p *CiphertextCommitmentEqualityProof) MarshalJSON() ([]byte, error) { + // Serialize the points and scalars to a format you prefer + return json.Marshal(map[string]interface{}{ + "y0": p.Y0.ToAffineCompressed(), // Assuming ToAffineCompressed returns a byte slice + "y1": p.Y1.ToAffineCompressed(), // Serialize Point to compressed bytes + "y2": p.Y2.ToAffineCompressed(), // Serialize Point to compressed bytes + "zs": p.Zs.Bytes(), // Serialize Scalar to bytes + "zx": p.Zx.Bytes(), // Serialize Scalar to bytes + "zr": p.Zr.Bytes(), // Serialize Scalar to bytes + }) +} + +// UnmarshalJSON for CiphertextCommitmentEqualityProof +func (p *CiphertextCommitmentEqualityProof) UnmarshalJSON(data []byte) error { + // Create a temporary structure to decode JSON + var temp struct { + Y0 []byte `json:"y0"` + Y1 []byte `json:"y1"` + Y2 []byte `json:"y2"` + Zs []byte `json:"zs"` + Zx []byte `json:"zx"` + Zr []byte `json:"zr"` + } + + // Unmarshal into the temp structure + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + ed25519 := curves.ED25519() + // Convert the byte arrays back into curve points and scalars + y0, err := ed25519.Point.FromAffineCompressed(temp.Y0) + if err != nil { + return err + } + y1, err := ed25519.Point.FromAffineCompressed(temp.Y1) + if err != nil { + return err + } + y2, err := ed25519.Point.FromAffineCompressed(temp.Y2) + if err != nil { + return err + } + zs, err := ed25519.Scalar.SetBytes(temp.Zs) + if err != nil { + return err + } + zx, err := ed25519.Scalar.SetBytes(temp.Zx) + if err != nil { + return err + } + zr, err := ed25519.Scalar.SetBytes(temp.Zr) + if err != nil { + return err + } + + // Assign the decoded values to the CiphertextCommitmentEqualityProof struct + p.Y0 = y0 + p.Y1 = y1 + p.Y2 = y2 + p.Zs = zs + p.Zx = zx + p.Zr = zr + + return nil +} diff --git a/pkg/zkproofs/ciphertext_commitment_equality_test.go b/pkg/zkproofs/ciphertext_commitment_equality_test.go new file mode 100644 index 0000000..4bffa2d --- /dev/null +++ b/pkg/zkproofs/ciphertext_commitment_equality_test.go @@ -0,0 +1,159 @@ +package zkproofs + +import ( + "crypto/rand" + "encoding/json" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "math/big" + "testing" +) + +func TestCiphertextCommitmentEqualityProof(t *testing.T) { + tests := []struct { + name string + sourceAmount uint64 + alternativePedersenCommitment bool + incorrectPedersenOpening bool + incorrectAmount bool + expectValid bool + }{ + { + name: "Valid Proof - Equal Amounts and Commitments", + sourceAmount: 100, + alternativePedersenCommitment: false, + expectValid: true, + }, + { + name: "Valid Proof - Equal Amounts and Alternative Commitments", + sourceAmount: 100, + alternativePedersenCommitment: true, + expectValid: true, + }, + { + name: "Invalid Proof - Different Pedersen Opening", + sourceAmount: 200, + alternativePedersenCommitment: false, + incorrectPedersenOpening: true, + expectValid: false, + }, + { + name: "Invalid Proof - Incorrect Amount in Pedersen Commitment", + sourceAmount: 200, + alternativePedersenCommitment: true, + incorrectPedersenOpening: false, + incorrectAmount: true, + expectValid: false, + }, + } + for _, tt := range tests { + tt := tt // Capture range variable + t.Run(tt.name, func(t *testing.T) { + // Key generation + sourcePrivateKey, err := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) + + // Encrypt the source amount + sourceCiphertext, sourceRandomness, err := eg.Encrypt(sourceKeypair.PublicKey, tt.sourceAmount) + assert.NoError(t, err, "Encryption should succeed for sourceCiphertext") + + var amountToSet uint64 + if tt.incorrectAmount { + amountToSet = tt.sourceAmount + 1 + } else { + amountToSet = tt.sourceAmount + } + scalarAmtValue := new(big.Int).SetUint64(amountToSet) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) + + pedersenCommitment := sourceCiphertext.C + + if !tt.alternativePedersenCommitment { + // Generate a random scalar r + randomFactor := curves.ED25519().Scalar.Random(rand.Reader) + + // Fixed base points G and H + G := eg.GetG() + H := eg.GetH() + // Compute the Pedersen commitment: C = r * H + x * G + rH := H.Mul(randomFactor) // r * H + xG := G.Mul(scalarAmount) // x * G + C := rH.Add(xG) + + pedersenCommitment = C + sourceRandomness = randomFactor + } + + if tt.incorrectPedersenOpening { + // Generate a random scalar r + randomFactor := curves.ED25519().Scalar.Random(rand.Reader) + sourceRandomness = randomFactor + } + + // Generate the proof + proof, err := NewCiphertextCommitmentEqualityProof( + sourceKeypair, + sourceCiphertext, + &sourceRandomness, + &scalarAmount, + ) + assert.NoError(t, err, "Proof generation should not fail") + + // VerifyCipherCipherEquality the proof + valid := VerifyCiphertextCommitmentEquality( + proof, + &sourceKeypair.PublicKey, + sourceCiphertext, + &pedersenCommitment, + ) + + if tt.expectValid { + assert.True(t, valid, "Proof verification should succeed") + } else { + assert.False(t, valid, "Proof verification should fail") + } + }) + } +} + +func TestCiphertextCommitmentEqualityProof_MarshalUnmarshalJSON(t *testing.T) { + sourcePrivateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) + + amount := uint64(232436) + // Encrypt the source amount + sourceCiphertext, sourceRandomness, err := eg.Encrypt(sourceKeypair.PublicKey, amount) + assert.NoError(t, err, "Encryption should succeed for sourceCiphertext") + + scalarAmtValue := new(big.Int).SetUint64(amount) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) + + // Create a sample CiphertextCommitmentEqualityProof + proof, err := NewCiphertextCommitmentEqualityProof( + sourceKeypair, + sourceCiphertext, + &sourceRandomness, + &scalarAmount, + ) + + // Marshal the proof to JSON + data, err := json.Marshal(proof) + require.NoError(t, err, "Marshaling should not produce an error") + + // Unmarshal the JSON back to a CiphertextCommitmentEqualityProof + var unmarshaled CiphertextCommitmentEqualityProof + err = json.Unmarshal(data, &unmarshaled) + require.NoError(t, err, "Unmarshaling should not produce an error") + + // Compare the original and unmarshaled proof + require.True(t, proof.Y0.Equal(unmarshaled.Y0), "Y0 points should be equal") + require.True(t, proof.Y1.Equal(unmarshaled.Y1), "Y1 points should be equal") + require.True(t, proof.Y2.Equal(unmarshaled.Y2), "Y2 points should be equal") + require.Equal(t, proof.Zs, unmarshaled.Zs, "Zs scalars should be equal") + require.Equal(t, proof.Zx, unmarshaled.Zx, "Zx scalars should be equal") + require.Equal(t, proof.Zr, unmarshaled.Zr, "Zr scalars should be equal") +} From ad1472b8adc3380625ddab15ea7c79d186cbd2c5 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 10 Oct 2024 17:09:54 -0700 Subject: [PATCH 05/30] - ciphertext validity + proofs --- pkg/zkproofs/ciphertext_validity.go | 145 +++++++++++++++++++++++ pkg/zkproofs/ciphertext_validity_test.go | 83 +++++++++++++ 2 files changed, 228 insertions(+) create mode 100644 pkg/zkproofs/ciphertext_validity.go create mode 100644 pkg/zkproofs/ciphertext_validity_test.go diff --git a/pkg/zkproofs/ciphertext_validity.go b/pkg/zkproofs/ciphertext_validity.go new file mode 100644 index 0000000..e96e424 --- /dev/null +++ b/pkg/zkproofs/ciphertext_validity.go @@ -0,0 +1,145 @@ +package zkproofs + +import ( + crand "crypto/rand" + "encoding/json" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + "math/big" +) + +// CiphertextValidityProof represents a zero-knowledge proof that a ciphertext is valid. +type CiphertextValidityProof struct { + Commitment1 curves.Point + Commitment2 curves.Point + Challenge curves.Scalar + Response1 curves.Scalar + Response2 curves.Scalar +} + +// NewCiphertextValidityProof generates a zero-knowledge proof that a ciphertext is properly encrypted. +func NewCiphertextValidityProof(message uint64, r *curves.Scalar, pubKey curves.Point, ct *elgamal.Ciphertext) *CiphertextValidityProof { + eg := elgamal.NewTwistedElgamal() + + var proof CiphertextValidityProof + H := eg.GetH() + G := eg.GetG() + + ed25519 := curves.ED25519() + // Convert message to a scalar + messageValue := new(big.Int).SetUint64(message) + x, _ := ed25519.Scalar.SetBigInt(messageValue) + + // Step 1: Generate random blinding factors for the proof + rBlind := ed25519.Scalar.Random(crand.Reader) // Blinding factor for random value r + xBlind := ed25519.Scalar.Random(crand.Reader) // Blinding factor for random value x + + // Step 2: Create commitments + rBlindH := H.Mul(rBlind) // rBlind * H + xBlindG := G.Mul(xBlind) // xBlind * G + Commitment1 := rBlindH.Add(xBlindG) // Commitment1 = rBlind * H + xBlind * G + + Commitment2 := pubKey.Mul(rBlind) // Commitment2 = rBlind * P + + // Step 3: Generate a challenge using the Fiat-Shamir heuristic. + // The challenge is basically just a hash of all the provided values. This locks in the values and makes sure that the proof cannot be for some other set of values. + hashData := append(Commitment1.ToAffineCompressed(), Commitment2.ToAffineCompressed()...) + hashData = append(hashData, ct.C.ToAffineCompressed()...) + hashData = append(hashData, ct.D.ToAffineCompressed()...) + challenge := ed25519.Scalar.Hash(hashData) + + // Step 4: Generate responses + Response1 := challenge.MulAdd(*r, rBlind) // Response1 = rBlind + challenge * r + Response2 := challenge.MulAdd(x, xBlind) // Response2 = xBlind + challenge * x + + // Store the proof + proof.Commitment1 = Commitment1 + proof.Commitment2 = Commitment2 + proof.Challenge = challenge + proof.Response1 = Response1 + proof.Response2 = Response2 + + return &proof +} + +// VerifyCiphertextValidityProof verifies the zero-knowledge proof that a ciphertext is valid. +func VerifyCiphertextValidityProof(proof *CiphertextValidityProof, pubKey curves.Point, ct *elgamal.Ciphertext) bool { + + eg := elgamal.NewTwistedElgamal() + H := eg.GetH() + G := eg.GetG() + + // Step 1: Recompute Commitment1 + response1H := H.Mul(proof.Response1) // response1 * H + response2G := G.Mul(proof.Response2) // response2 * G + challengeC := ct.C.Mul(proof.Challenge) // challenge * C + recomputedCommitment1 := response1H.Add(response2G).Sub(challengeC) + + // Step 2: Recompute Commitment2 + challengeD := ct.D.Mul(proof.Challenge) // challenge * D + recomputedCommitment2 := pubKey.Mul(proof.Response1).Sub(challengeD) + + // Step 3: Check if the recomputed commitments match the original commitments + return recomputedCommitment1.Equal(proof.Commitment1) && recomputedCommitment2.Equal(proof.Commitment2) +} + +// MarshalJSON for CiphertextValidityProof +func (p *CiphertextValidityProof) MarshalJSON() ([]byte, error) { + // Serialize the points and scalars to a format you prefer + return json.Marshal(map[string]interface{}{ + "commitment1": p.Commitment1.ToAffineCompressed(), // Assuming ToAffineCompressed returns a byte slice + "commitment2": p.Commitment2.ToAffineCompressed(), + "challenge": p.Challenge.Bytes(), // Serialize Scalar to bytes + "response1": p.Response1.Bytes(), // Serialize Scalar to bytes + "response2": p.Response2.Bytes(), // Serialize Scalar to bytes + }) +} + +// UnmarshalJSON for CiphertextValidityProof +func (p *CiphertextValidityProof) UnmarshalJSON(data []byte) error { + // Create a temporary structure to decode JSON + var temp struct { + Commitment1 []byte `json:"commitment1"` + Commitment2 []byte `json:"commitment2"` + Challenge []byte `json:"challenge"` + Response1 []byte `json:"response1"` + Response2 []byte `json:"response2"` + } + + // Unmarshal into the temp structure + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + ed25519 := curves.ED25519() + // Convert the byte arrays back into curve points and scalars + commitment1, err := ed25519.Point.FromAffineCompressed(temp.Commitment1) + if err != nil { + return err + } + commitment2, err := ed25519.Point.FromAffineCompressed(temp.Commitment2) + if err != nil { + return err + } + challenge, err := ed25519.Scalar.SetBytes(temp.Challenge) + if err != nil { + return err + } + response1, err := ed25519.Scalar.SetBytes(temp.Response1) + if err != nil { + return err + } + response2, err := ed25519.Scalar.SetBytes(temp.Response2) + if err != nil { + return err + } + + // Assign the decoded values to the CiphertextValidityProof struct + p.Commitment1 = commitment1 + p.Commitment2 = commitment2 + p.Challenge = challenge + p.Response1 = response1 + p.Response2 = response2 + + return nil +} diff --git a/pkg/zkproofs/ciphertext_validity_test.go b/pkg/zkproofs/ciphertext_validity_test.go new file mode 100644 index 0000000..4f80f0a --- /dev/null +++ b/pkg/zkproofs/ciphertext_validity_test.go @@ -0,0 +1,83 @@ +package zkproofs + +import ( + "encoding/json" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + "github.com/stretchr/testify/require" + "testing" +) + +func TestValidityProof(t *testing.T) { + privateKey, err := elgamal.GenerateKey() + altPrivateKey, err := elgamal.GenerateKey() + + require.Nil(t, err, "Should not have error here") + + eg := elgamal.NewTwistedElgamal() + keys, err := eg.KeyGen(*privateKey, TestDenom) + altKeys, err := eg.KeyGen(*altPrivateKey, TestDenom) + + message12 := uint64(12) + ciphertext12, randomness12, err := eg.Encrypt(keys.PublicKey, message12) + require.Nil(t, err, "Should not have error here") + + proof12 := NewCiphertextValidityProof(message12, &randomness12, keys.PublicKey, ciphertext12) + + validated := VerifyCiphertextValidityProof(proof12, keys.PublicKey, ciphertext12) + require.True(t, validated, "Validating with the correct parameters should validate as true") + + validated = VerifyCiphertextValidityProof(proof12, altKeys.PublicKey, ciphertext12) + require.False(t, validated, "Validating with the wrong PublicKey should validate as false") + + // Encrypt a message (e.g., x = 42) + message42 := uint64(42) + ciphertext42, randomness42, _ := eg.Encrypt(keys.PublicKey, message42) + + validated = VerifyCiphertextValidityProof(proof12, altKeys.PublicKey, ciphertext42) + require.False(t, validated, "Validating with the wrong ciphertext should validate as false") + + // Generate proof using the wrong pubkey + wrongProof := NewCiphertextValidityProof(message12, &randomness12, altKeys.PublicKey, ciphertext12) + validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) + require.False(t, validated, "Proof generated with the wrong PublicKey should validate as false") + + validated = VerifyCiphertextValidityProof(wrongProof, altKeys.PublicKey, ciphertext12) + require.False(t, validated, "Proof generated with the wrong PublicKey should validate as false even"+ + " when using the same pubkey to validate") + + // Generate proof using the wrong randomness + wrongProof = NewCiphertextValidityProof(message12, &randomness42, keys.PublicKey, ciphertext12) + validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) + require.False(t, validated, "Proof generated with the wrong randomness should validate as false") + + // Generate proof with the wrong ciphertext + wrongProof = NewCiphertextValidityProof(message42, &randomness42, keys.PublicKey, ciphertext12) + validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) + require.False(t, validated, "Proof generated with the wrong ciphertext should validate as false") +} + +func TestCiphertextValidityProof_MarshalUnmarshalJSON(t *testing.T) { + privateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + keys, _ := eg.KeyGen(*privateKey, TestDenom) + + message12 := uint64(12) + ciphertext12, randomness12, _ := eg.Encrypt(keys.PublicKey, message12) + + original := NewCiphertextValidityProof(message12, &randomness12, keys.PublicKey, ciphertext12) + // Marshal the proof to JSON + data, err := json.Marshal(original) + require.NoError(t, err, "Marshaling should not produce an error") + + // Unmarshal the JSON back to a CiphertextValidityProof + var unmarshaled CiphertextValidityProof + err = json.Unmarshal(data, &unmarshaled) + require.NoError(t, err, "Unmarshaling should not produce an error") + + // Compare the original and unmarshaled proof + require.True(t, original.Commitment1.Equal(unmarshaled.Commitment1), "Commitment1 points should be equal") + require.True(t, original.Commitment2.Equal(unmarshaled.Commitment2), "Commitment2 points should be equal") + require.Equal(t, original.Challenge, unmarshaled.Challenge, "Challenge scalars should be equal") + require.Equal(t, original.Response1, unmarshaled.Response1, "Response1 scalars should be equal") + require.Equal(t, original.Response2, unmarshaled.Response2, "Response2 scalars should be equal") +} From 0dbb17da4a9512d05a591e7de1a9a5710b567814 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 10 Oct 2024 17:18:51 -0700 Subject: [PATCH 06/30] - pubkey validity proofs + tests --- pkg/zkproofs/pubkey_validity.go | 117 +++++++++++++++++++++++++++ pkg/zkproofs/pubkey_validity_test.go | 55 +++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 pkg/zkproofs/pubkey_validity.go create mode 100644 pkg/zkproofs/pubkey_validity_test.go diff --git a/pkg/zkproofs/pubkey_validity.go b/pkg/zkproofs/pubkey_validity.go new file mode 100644 index 0000000..a9659f4 --- /dev/null +++ b/pkg/zkproofs/pubkey_validity.go @@ -0,0 +1,117 @@ +package zkproofs + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/json" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" +) + +// PubKeyValidityProof The Public Key Validation goes as follows: +// For some keypair s (privKey) and s_inv*H (pubKey) +// 1. Generate some random scalar y +// 2. We agree on some random challenge c, which is the hash of the commitment y. +// 3. The prover constructs the proof z = c*s_inv + y. which masks s_inv +// 4. The verifier checks that z*H = c*Pubkey + y*H. This expands to c*s_inv*H + y*H = c*(s_inv*H) + y*H +type PubKeyValidityProof struct { + Y curves.Point + Z curves.Scalar +} + +// NewPubKeyValidityProof generates the sigma protocol proof +// The proof here is that the creator of this PubKey also knows the corresponding PrivateKey +func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) *PubKeyValidityProof { + eg := elgamal.NewTwistedElgamal() + H := eg.GetH() + // Prover generates a random scalar y + y := curves.ED25519().Scalar.Random(rand.Reader) + + // Commitment Y = y * H + Y := H.Mul(y) + + // Generate challenge c based on P and Y + c := generateChallenge(pubKey, Y) + + // Compute sInv = s^{-1} + sInv, _ := privKey.Invert() + + // Response z = c * sInv + y + z := c.MulAdd(sInv, y) // z = c * sInv + y + + return &PubKeyValidityProof{ + Y: Y, + Z: z, + } +} + +// ValidatePubKeyValidityProof verifies the validity of the proof +func ValidatePubKeyValidityProof(pubKey curves.Point, proof PubKeyValidityProof) bool { + eg := elgamal.NewTwistedElgamal() + H := eg.GetH() + // Recompute the challenge c + c := generateChallenge(pubKey, proof.Y) + + // Compute lhs = z * H + lhs := H.Mul(proof.Z) // lhs = z * H + + // Compute rhs = c * P + Y + cP := pubKey.Mul(c) // c * P + rhs := cP.Add(proof.Y) // rhs = c * P + Y + + // Check if z * H == c * P + Y + return lhs.Equal(rhs) +} + +// generateChallenge generates a challenge c by hashing P and Y +func generateChallenge(P, Y curves.Point) curves.Scalar { + // Hash P and Y using SHA-256 + hash := sha256.New() + + hash.Write(P.ToAffineCompressed()) + hash.Write(Y.ToAffineCompressed()) + digest := hash.Sum(nil) + + // Convert hash output into a scalar + return curves.ED25519().Scalar.Hash(digest) +} + +// MarshalJSON for PubKeyValidityProof +func (p *PubKeyValidityProof) MarshalJSON() ([]byte, error) { + // Serialize the points and scalars to a format you prefer + return json.Marshal(map[string]interface{}{ + "y": p.Y.ToAffineCompressed(), + "z": p.Z.Bytes(), + }) +} + +// UnmarshalJSON for PubKeyValidityProof +func (p *PubKeyValidityProof) UnmarshalJSON(data []byte) error { + // Create a temporary structure to decode JSON + var temp struct { + Y []byte `json:"y"` + Z []byte `json:"z"` + } + + // Unmarshal into the temp structure + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + ed25519 := curves.ED25519() + // Convert the byte arrays back into curve points and scalars + y, err := ed25519.Point.FromAffineCompressed(temp.Y) + if err != nil { + return err + } + z, err := ed25519.Scalar.SetBytes(temp.Z) + if err != nil { + return err + } + + // Assign the decoded values to the PubKeyValidityProof struct + p.Y = y + p.Z = z + + return nil +} diff --git a/pkg/zkproofs/pubkey_validity_test.go b/pkg/zkproofs/pubkey_validity_test.go new file mode 100644 index 0000000..cafcba7 --- /dev/null +++ b/pkg/zkproofs/pubkey_validity_test.go @@ -0,0 +1,55 @@ +package zkproofs + +import ( + "encoding/json" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + "github.com/stretchr/testify/require" + "testing" +) + +func TestPubKeyValidityProof(t *testing.T) { + privateKey, err := elgamal.GenerateKey() + altPrivateKey, err := elgamal.GenerateKey() + + require.Nil(t, err, "Should not have error here") + + eg := elgamal.NewTwistedElgamal() + keys, err := eg.KeyGen(*privateKey, TestDenom) + altKeys, err := eg.KeyGen(*altPrivateKey, TestDenom) + require.Nil(t, err, "Should not have error here") + + // Prove knowledge of the private key + proof := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) + + // Verify the proof + valid := ValidatePubKeyValidityProof(keys.PublicKey, *proof) + require.True(t, valid, "Valid Proof should be validated as true") + + invalid := ValidatePubKeyValidityProof(altKeys.PublicKey, *proof) + require.False(t, invalid, "Proof should be invalid when trying to validate wrong PublicKey") + + // Generate proof with the wrong private key. + badProof := NewPubKeyValidityProof(keys.PublicKey, altKeys.PrivateKey) + invalid = ValidatePubKeyValidityProof(keys.PublicKey, *badProof) + require.False(t, invalid, "Proof generated with wrong Privkey should be validated as false.") +} + +func TestPubKeyValidityProof_MarshalUnmarshalJSON(t *testing.T) { + privateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + keys, _ := eg.KeyGen(*privateKey, TestDenom) + + original := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) + // Marshal the proof to JSON + data, err := json.Marshal(original) + require.NoError(t, err, "Marshaling should not produce an error") + + // Unmarshal the JSON back to a PubKeyValidityProof + var unmarshaled PubKeyValidityProof + err = json.Unmarshal(data, &unmarshaled) + require.NoError(t, err, "Unmarshaling should not produce an error") + + // Compare the original and unmarshaled proof + require.True(t, original.Y.Equal(unmarshaled.Y), "Y points should be equal") + require.Equal(t, original.Z, unmarshaled.Z, "Z scalars should be equal") +} From 4690e7a715ec8f2d5a10dac12675f3170d8a1fa5 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 10 Oct 2024 17:20:21 -0700 Subject: [PATCH 07/30] - rename method for consistency with the rest of the lib --- pkg/zkproofs/pubkey_validity.go | 4 ++-- pkg/zkproofs/pubkey_validity_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/zkproofs/pubkey_validity.go b/pkg/zkproofs/pubkey_validity.go index a9659f4..01128cb 100644 --- a/pkg/zkproofs/pubkey_validity.go +++ b/pkg/zkproofs/pubkey_validity.go @@ -45,8 +45,8 @@ func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) *PubKeyV } } -// ValidatePubKeyValidityProof verifies the validity of the proof -func ValidatePubKeyValidityProof(pubKey curves.Point, proof PubKeyValidityProof) bool { +// VerifyPubKeyValidityProof verifies the validity of the proof +func VerifyPubKeyValidityProof(pubKey curves.Point, proof PubKeyValidityProof) bool { eg := elgamal.NewTwistedElgamal() H := eg.GetH() // Recompute the challenge c diff --git a/pkg/zkproofs/pubkey_validity_test.go b/pkg/zkproofs/pubkey_validity_test.go index cafcba7..ff8c2d4 100644 --- a/pkg/zkproofs/pubkey_validity_test.go +++ b/pkg/zkproofs/pubkey_validity_test.go @@ -22,15 +22,15 @@ func TestPubKeyValidityProof(t *testing.T) { proof := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) // Verify the proof - valid := ValidatePubKeyValidityProof(keys.PublicKey, *proof) + valid := VerifyPubKeyValidityProof(keys.PublicKey, *proof) require.True(t, valid, "Valid Proof should be validated as true") - invalid := ValidatePubKeyValidityProof(altKeys.PublicKey, *proof) + invalid := VerifyPubKeyValidityProof(altKeys.PublicKey, *proof) require.False(t, invalid, "Proof should be invalid when trying to validate wrong PublicKey") // Generate proof with the wrong private key. badProof := NewPubKeyValidityProof(keys.PublicKey, altKeys.PrivateKey) - invalid = ValidatePubKeyValidityProof(keys.PublicKey, *badProof) + invalid = VerifyPubKeyValidityProof(keys.PublicKey, *badProof) require.False(t, invalid, "Proof generated with wrong Privkey should be validated as false.") } From 1e078548f40c2b427caf34803798aa198c52de67 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 10:47:13 -0700 Subject: [PATCH 08/30] - range proofs + tests --- go.mod | 2 + go.sum | 4 + pkg/zkproofs/range.go | 130 +++++++++++++++++++++++++++++ pkg/zkproofs/range_test.go | 167 +++++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 pkg/zkproofs/range.go create mode 100644 pkg/zkproofs/range_test.go diff --git a/go.mod b/go.mod index b34a293..1b02239 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/bwesterb/go-ristretto v1.2.3 github.com/coinbase/kryptology v1.8.0 github.com/ethereum/go-ethereum v1.10.26 + github.com/gtank/merlin v0.1.1 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.27.0 ) @@ -16,6 +17,7 @@ require ( github.com/consensys/gnark-crypto v0.5.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/pretty v0.1.0 // indirect + github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.25.0 // indirect diff --git a/go.sum b/go.sum index 1c49698..28328f4 100644 --- a/go.sum +++ b/go.sum @@ -29,6 +29,8 @@ github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqB github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -42,6 +44,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= diff --git a/pkg/zkproofs/range.go b/pkg/zkproofs/range.go new file mode 100644 index 0000000..6762ac0 --- /dev/null +++ b/pkg/zkproofs/range.go @@ -0,0 +1,130 @@ +package zkproofs + +import ( + crand "crypto/rand" + "encoding/json" + "github.com/coinbase/kryptology/pkg/bulletproof" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/gtank/merlin" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" +) + +const rangeDomain = "rangeDomain" +const ippDomain = "ippDomain" + +type RangeProof struct { + Proof *bulletproof.RangeProof + Randomness curves.Point + UpperBound int +} + +// NewRangeProof a range proof for some ciphertext that proves the value is between 0 and 2^upperBound +// Note that while this proof generator takes in the plaintext of the value, it will only work in conjunction with a +// ciphertext that encrypts value using the given randomness. +func NewRangeProof(upperBound, value int, randomness curves.Scalar) (*RangeProof, error) { + curve := curves.ED25519() + prover, err := bulletproof.NewRangeProver(upperBound, getRangeDomain(), getIppDomain(), *curve) + if err != nil { + return nil, err + } + + eg := elgamal.NewTwistedElgamal() + g := eg.GetG() + h := eg.GetH() + u := curve.Point.Random(crand.Reader) + transcript := getTranscript() + + vScalar := curve.Scalar.New(value) + proof, err := prover.Prove(vScalar, randomness, upperBound, g, h, u, transcript) + if err != nil { + return nil, err + } + return &RangeProof{ + Proof: proof, + Randomness: u, + UpperBound: upperBound, + }, nil +} + +// VerifyRangeProof verifies the range proof for the given ciphertext +func VerifyRangeProof(proof *RangeProof, ciphertext elgamal.Ciphertext) (bool, error) { + curve := curves.ED25519() + // Verifier gets the proof, the commitment, the generators to verify the value is within the range + verifier, err := bulletproof.NewRangeVerifier(proof.UpperBound, getRangeDomain(), getIppDomain(), *curve) + if err != nil { + return false, err + } + + eg := elgamal.NewTwistedElgamal() + g := eg.GetG() + h := eg.GetH() + + verified, err := verifier.Verify(proof.Proof, ciphertext.C, g, h, proof.Randomness, proof.UpperBound, getTranscript()) + if err != nil { + return false, err + } + + return verified, nil +} + +func getRangeDomain() []byte { + return []byte(rangeDomain) +} + +func getIppDomain() []byte { + return []byte(ippDomain) +} + +func getTranscript() *merlin.Transcript { + return merlin.NewTranscript("proof") +} + +// MarshalJSON for RangeProof +func (r *RangeProof) MarshalJSON() ([]byte, error) { + // Use the MarshalBinary method to convert the bulletproof RangeProof to a byte array + binaryProof := r.Proof.MarshalBinary() + + // Serialize the entire struct, including the binary proof + return json.Marshal(map[string]interface{}{ + "proof": binaryProof, + "randomness": r.Randomness.ToAffineCompressed(), // Serialize randomness as a point + "upper_bound": r.UpperBound, // Serialize the upper bound as is + }) +} + +// UnmarshalJSON for RangeProof +func (r *RangeProof) UnmarshalJSON(data []byte) error { + // Temporary structure to hold the incoming JSON + var temp struct { + Proof []byte `json:"proof"` + Randomness []byte `json:"randomness"` + UpperBound int `json:"upper_bound"` + } + + // Unmarshal the JSON into the temp structure + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + ed25519 := curves.ED25519() + + // Initialize the RangeProof with the appropriate curve using NewRangeProof + r.Proof = bulletproof.NewRangeProof(ed25519) // Using the correct curve + + // Unmarshal the proof using the UnmarshalBinary method, which will populate all fields + if err := r.Proof.UnmarshalBinary(temp.Proof); err != nil { + return err + } + + // Unmarshal the randomness field + randomness, err := ed25519.Point.FromAffineCompressed(temp.Randomness) + if err != nil { + return err + } + r.Randomness = randomness + + // Set the upper bound + r.UpperBound = temp.UpperBound + + return nil +} diff --git a/pkg/zkproofs/range_test.go b/pkg/zkproofs/range_test.go new file mode 100644 index 0000000..7876b30 --- /dev/null +++ b/pkg/zkproofs/range_test.go @@ -0,0 +1,167 @@ +package zkproofs + +import ( + crand "crypto/rand" + "github.com/coinbase/kryptology/pkg/bulletproof" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/gtank/merlin" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + "github.com/stretchr/testify/require" + "testing" +) + +// Coinbase Kryptology's bulletproof package is used to generate range proofs +func TestValueIsInRange(t *testing.T) { + curve := curves.ED25519() + value := 100 + v := curve.Scalar.New(value) + n := 64 // the range is [0, 2^64] + + privateKey, err := elgamal.GenerateKey() + require.Nil(t, err, "Error generating private key") + + eg := elgamal.NewTwistedElgamal() + keyPair, err := eg.KeyGen(*privateKey, TestDenom) + require.Nil(t, err, "Error generating key pair") + + ciphertext, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(value)) + + prover, err := bulletproof.NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) + require.NoError(t, err) + g := eg.GetG() + h := eg.GetH() + u := curve.Point.Random(crand.Reader) + transcript := merlin.NewTranscript("test") + proof, err := prover.Prove(v, gamma, n, g, h, u, transcript) + require.NoError(t, err) + + // Verifier gets the proof, the commitment, the generators to verify the value is within the range + verifier, err := bulletproof.NewRangeVerifier(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) + require.NoError(t, err) + + transcriptVerifier := merlin.NewTranscript("test") + verified, err := verifier.Verify(proof, ciphertext.C, g, h, u, n, transcriptVerifier) + require.NoError(t, err) + require.True(t, verified) + + ciphertext101, _, err := eg.Encrypt(keyPair.PublicKey, uint64(101)) + require.Nil(t, err) + verified, err = verifier.Verify(proof, ciphertext101.C, g, h, u, n, transcriptVerifier) + require.Error(t, err) + require.False(t, verified) +} +func TestRangeAttacksAreInfeasible(t *testing.T) { + curve := curves.ED25519() + value := 100 + v := curve.Scalar.New(value) + n := 64 // the range is [0, 2^64] + + privateKey, err := elgamal.GenerateKey() + require.Nil(t, err, "Error generating private key") + + eg := elgamal.NewTwistedElgamal() + keyPair, err := eg.KeyGen(*privateKey, TestDenom) + require.Nil(t, err, "Error generating key pair") + + ciphertext, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(value)) + + prover, err := bulletproof.NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) + require.NoError(t, err) + g := eg.GetG() + h := eg.GetH() + u := curve.Point.Random(crand.Reader) + transcript := merlin.NewTranscript("test") + proof, err := prover.Prove(v, gamma, n, g, h, u, transcript) + require.NoError(t, err) + + // for 90 to 110 generate ciphertexts and see if we can guess the encrypted value + for i := 90; i < 110; i++ { + transcriptVerifier := merlin.NewTranscript("test") + ct, _, e := eg.Encrypt(keyPair.PublicKey, uint64(i)) + require.NoError(t, e) + + verifier, e := bulletproof.NewRangeVerifier(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) + require.NoError(t, e) + + verified, _ := verifier.Verify(proof, ct.C, g, h, u, n, transcriptVerifier) + if verified { + t.Errorf("Attack successful: the number is %d", i) + } + } + + // We know already that the value is in the range [0, 2^64]. For range [2^2 to 2^63], + // generate verifier for each range and see if we can narrow the range in which the value is. + // Starting from 2^2 as bulletptoof panics for 2^0 and 2^1 + for i := 2; i < n; i++ { + // Verifier gets the proof, the commitment, the generators to verify the value is within the range + verifier, e := bulletproof.NewRangeVerifier(i, []byte("rangeDomain"), []byte("ippDomain"), *curve) + require.NoError(t, e) + + transcriptVerifier := merlin.NewTranscript("test") + + require.NoError(t, e) + verified, _ := verifier.Verify(proof, ciphertext.C, g, h, u, i, transcriptVerifier) + if verified { + t.Errorf("Attack successful: the number is in the range 2^%d", i) + } + } +} + +func TestRangeVerifyNotInRange(t *testing.T) { + curve := curves.ED25519() + n := 2 + prover, err := bulletproof.NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) + require.NoError(t, err) + v := curve.Scalar.New(100) + gamma := curve.Scalar.Random(crand.Reader) + g := curve.Point.Random(crand.Reader) + h := curve.Point.Random(crand.Reader) + u := curve.Point.Random(crand.Reader) + transcript := merlin.NewTranscript("test") + _, err = prover.Prove(v, gamma, n, g, h, u, transcript) + require.Error(t, err) +} + +func TestRangeVerifyNotInRangeNegativeValue(t *testing.T) { + curve := curves.ED25519() + n := 64 + prover, err := bulletproof.NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) + require.NoError(t, err) + v := curve.Scalar.New(-100) + gamma := curve.Scalar.Random(crand.Reader) + g := curve.Point.Random(crand.Reader) + h := curve.Point.Random(crand.Reader) + u := curve.Point.Random(crand.Reader) + transcript := merlin.NewTranscript("test") + _, err = prover.Prove(v, gamma, n, g, h, u, transcript) + require.Error(t, err) +} + +func TestRangeProofs(t *testing.T) { + value := 100 + n := 64 // the range is [0, 2^64] + + privateKey, err := elgamal.GenerateKey() + require.Nil(t, err, "Error generating private key") + + eg := elgamal.NewTwistedElgamal() + keyPair, err := eg.KeyGen(*privateKey, TestDenom) + require.Nil(t, err, "Error generating key pair") + + ciphertext, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(value)) + + proof, err := NewRangeProof(n, value, gamma) + require.Nil(t, err) + + verified, err := VerifyRangeProof(proof, *ciphertext) + require.NoError(t, err) + require.True(t, verified) + + // Check that a ciphertext with a different value cannot use the same proof to verify as true, even if it meets the requirements. + ciphertext101, _, err := eg.Encrypt(keyPair.PublicKey, uint64(101)) + require.Nil(t, err) + + verified, err = VerifyRangeProof(proof, *ciphertext101) + require.Error(t, err) + require.False(t, verified) +} From 3049ff8242e129f1d23341e6b493471814deb467 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 11:10:06 -0700 Subject: [PATCH 09/30] - marshaling test --- pkg/zkproofs/range_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pkg/zkproofs/range_test.go b/pkg/zkproofs/range_test.go index 7876b30..c457d8f 100644 --- a/pkg/zkproofs/range_test.go +++ b/pkg/zkproofs/range_test.go @@ -2,6 +2,7 @@ package zkproofs import ( crand "crypto/rand" + "encoding/json" "github.com/coinbase/kryptology/pkg/bulletproof" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/gtank/merlin" @@ -165,3 +166,35 @@ func TestRangeProofs(t *testing.T) { require.Error(t, err) require.False(t, verified) } + +// We test marshaling and unmarshaling of the range proof this way as bulletproof.RangeProof does not implement Equals +// and particular fields are not exported. +func TestRangeProofsWithMarshaling(t *testing.T) { + value := 100 + n := 64 // the range is [0, 2^64] + + privateKey, err := elgamal.GenerateKey() + require.Nil(t, err, "Error generating private key") + + eg := elgamal.NewTwistedElgamal() + keyPair, err := eg.KeyGen(*privateKey, TestDenom) + require.Nil(t, err, "Error generating key pair") + + ciphertext, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(value)) + + proof, err := NewRangeProof(n, value, gamma) + require.Nil(t, err) + + // Marshal the proof to JSON + marshaledProof, err := json.Marshal(proof) + require.NoError(t, err, "Marshaling should not produce an error") + + // Unmarshal the JSON back to a RangeProof + var unmarshaled RangeProof + err = json.Unmarshal(marshaledProof, &unmarshaled) + require.NoError(t, err, "Unmarshaling should not produce an error") + + verified, err := VerifyRangeProof(&unmarshaled, *ciphertext) + require.NoError(t, err) + require.True(t, verified) +} From a1594f35362e202287a47124ecc092a8ce96f05c Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 11:36:37 -0700 Subject: [PATCH 10/30] - zero balance proofs + tests --- pkg/zkproofs/zero_balance.go | 140 ++++++++++++++++++++++++++++++ pkg/zkproofs/zero_balance_test.go | 94 ++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 pkg/zkproofs/zero_balance.go create mode 100644 pkg/zkproofs/zero_balance_test.go diff --git a/pkg/zkproofs/zero_balance.go b/pkg/zkproofs/zero_balance.go new file mode 100644 index 0000000..424b764 --- /dev/null +++ b/pkg/zkproofs/zero_balance.go @@ -0,0 +1,140 @@ +package zkproofs + +import ( + "crypto/rand" + "encoding/json" + "github.com/coinbase/kryptology/pkg/core/curves" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" +) + +// ZeroBalanceProof represents a zero-knowledge proof that a ciphertext encrypts 0 number. +type ZeroBalanceProof struct { + Yp curves.Point + Yd curves.Point + Z curves.Scalar +} + +// NewZeroBalanceProof generates a zero-knowledge proof that a ciphertext encrypts 0 number. +func NewZeroBalanceProof( + keypair *elgamal.KeyPair, + ciphertext *elgamal.Ciphertext, +) (*ZeroBalanceProof, error) { + + // Extract necessary values + P := keypair.PublicKey + s := keypair.PrivateKey + D := ciphertext.D + + // Generate random masking factor y + y := curves.ED25519().Scalar.Random(rand.Reader) + + // Compute Yp = y * P and Yd = y * D + Yp := P.Mul(y) + Yd := D.Mul(y) + + // Append commitments to the transcript + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("Yp", Yp.ToAffineCompressed()) + transcript.AppendMessage("Yd", Yd.ToAffineCompressed()) + + // Generate the challenge scalar from the transcript + c := transcript.ChallengeScalar() + + // Compute Z = c * s + y + Z := c.Mul(s).Add(y) + + return &ZeroBalanceProof{ + Yp: Yp, + Yd: Yd, + Z: Z, + }, nil +} + +// VerifyZeroProof verifies the validity of the proof +func VerifyZeroProof( + proof *ZeroBalanceProof, + pubKey *curves.Point, + ciphertext *elgamal.Ciphertext, +) bool { + // Extract necessary values + P := *pubKey + C := ciphertext.C + D := ciphertext.D + + eg := elgamal.NewTwistedElgamal() + H := eg.GetH() + + // Append commitments to the transcript + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("Yp", proof.Yp.ToAffineCompressed()) + transcript.AppendMessage("Yd", proof.Yd.ToAffineCompressed()) + + // Generate the challenge scalar from the transcript + c := transcript.ChallengeScalar() + + // VerifyCipherCipherEquality z * P == c * H + Yp + lhsYp := P.Mul(proof.Z) // z * P + cH := H.Mul(c) // c * H + rhsYp := cH.Add(proof.Yp) // c * H + Yp + + if !lhsYp.Equal(rhsYp) { + return false + } + + // VerifyCipherCipherEquality z * D == c * C + Yd + lhsYd := D.Mul(proof.Z) // z * D + cC := C.Mul(c) // c * C + rhsYd := cC.Add(proof.Yd) // c * C + Yd + + if !lhsYd.Equal(rhsYd) { + return false + } + return true +} + +// MarshalJSON for ZeroBalanceProof +func (p *ZeroBalanceProof) MarshalJSON() ([]byte, error) { + // Serialize the points and scalars to a format you prefer + return json.Marshal(map[string]interface{}{ + "yp": p.Yp.ToAffineCompressed(), + "yd": p.Yd.ToAffineCompressed(), + "z": p.Z.Bytes(), + }) +} + +// UnmarshalJSON for ZeroBalanceProof +func (p *ZeroBalanceProof) UnmarshalJSON(data []byte) error { + // Create a temporary structure to decode JSON + var temp struct { + Yp []byte `json:"yp"` + Yd []byte `json:"yd"` + Z []byte `json:"z"` + } + + // Unmarshal into the temp structure + if err := json.Unmarshal(data, &temp); err != nil { + return err + } + + ed25519 := curves.ED25519() + // Convert the byte arrays back into curve points and scalars + yp, err := ed25519.Point.FromAffineCompressed(temp.Yp) + if err != nil { + return err + } + yd, err := ed25519.Point.FromAffineCompressed(temp.Yd) + if err != nil { + return err + } + z, err := ed25519.Scalar.SetBytes(temp.Z) + if err != nil { + return err + } + + // Assign the decoded values to the ZeroBalanceProof struct + p.Yp = yp + p.Yd = yd + p.Z = z + + return nil +} diff --git a/pkg/zkproofs/zero_balance_test.go b/pkg/zkproofs/zero_balance_test.go new file mode 100644 index 0000000..ed438e3 --- /dev/null +++ b/pkg/zkproofs/zero_balance_test.go @@ -0,0 +1,94 @@ +package zkproofs + +import ( + "encoding/json" + "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + "github.com/stretchr/testify/require" + "testing" +) + +func TestZeroBalanceProof(t *testing.T) { + tests := []struct { + name string + encryptAmount uint64 + useDifferentPubKey bool + expectValid bool + }{ + { + name: "Valid Proof - Correct Encryption and Commitment", + encryptAmount: 0, + useDifferentPubKey: false, + expectValid: true, + }, + { + name: "Invalid Proof - Non-Zero Value", + encryptAmount: 10000, + useDifferentPubKey: false, + expectValid: false, + }, + { + name: "Invalid Proof - Different Public Key", + encryptAmount: 0, + useDifferentPubKey: true, + expectValid: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Setup keypair + privateKey, err := elgamal.GenerateKey() + altPrivateKey, err := elgamal.GenerateKey() + + eg := elgamal.NewTwistedElgamal() + keypair, err := eg.KeyGen(*privateKey, TestDenom) + alternativeKeypair, err := eg.KeyGen(*altPrivateKey, TestDenom) + + actualPublicKey := keypair.PublicKey + if tt.useDifferentPubKey { + actualPublicKey = alternativeKeypair.PublicKey + } + + ciphertext, _, err := eg.Encrypt(actualPublicKey, tt.encryptAmount) + if err != nil { + t.Fatalf("Failed to encrypt amount: %v", err) + } + + // Generate ZeroBalanceProof + proof, err := NewZeroBalanceProof(keypair, ciphertext) + if err != nil { + t.Fatalf("Failed to generate proof: %v", err) + } + + // Verify the proof + valid := VerifyZeroProof(proof, &keypair.PublicKey, ciphertext) + if valid != tt.expectValid { + t.Errorf("Expected proof validity to be %v, got %v", tt.expectValid, valid) + } + }) + } +} + +func TestZeroBalanceProof_MarshalUnmarshalJSON(t *testing.T) { + privateKey, _ := elgamal.GenerateKey() + + eg := elgamal.NewTwistedElgamal() + keypair, _ := eg.KeyGen(*privateKey, TestDenom) + + ciphertext, _, _ := eg.Encrypt(keypair.PublicKey, 0) + original, err := NewZeroBalanceProof(keypair, ciphertext) + + // Marshal the proof to JSON + data, err := json.Marshal(original) + require.NoError(t, err, "Marshaling should not produce an error") + + // Unmarshal the JSON back to a ZeroBalanceProof + var unmarshaled ZeroBalanceProof + err = json.Unmarshal(data, &unmarshaled) + require.NoError(t, err, "Unmarshaling should not produce an error") + + // Compare the original and unmarshaled proof + require.True(t, original.Yp.Equal(unmarshaled.Yp), "Yp points should be equal") + require.True(t, original.Yd.Equal(unmarshaled.Yd), "Yd points should be equal") + require.Equal(t, original.Z, unmarshaled.Z, "Z scalars should be equal") +} From 7d5d44cc114569135ffb280e9daf4a3bcba84132 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 14:06:09 -0700 Subject: [PATCH 11/30] - invalid inputs tests --- pkg/zkproofs/zero_balance.go | 13 +++ pkg/zkproofs/zero_balance_test.go | 183 ++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/pkg/zkproofs/zero_balance.go b/pkg/zkproofs/zero_balance.go index 424b764..d7f0449 100644 --- a/pkg/zkproofs/zero_balance.go +++ b/pkg/zkproofs/zero_balance.go @@ -3,6 +3,7 @@ package zkproofs import ( "crypto/rand" "encoding/json" + "errors" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" ) @@ -19,6 +20,13 @@ func NewZeroBalanceProof( keypair *elgamal.KeyPair, ciphertext *elgamal.Ciphertext, ) (*ZeroBalanceProof, error) { + if keypair == nil || keypair.PublicKey == nil || keypair.PrivateKey == nil { + return nil, errors.New("keypair is invalid") + } + + if ciphertext == nil || ciphertext.D == nil || ciphertext.C == nil { + return nil, errors.New("ciphertext is invalid") + } // Extract necessary values P := keypair.PublicKey @@ -56,6 +64,11 @@ func VerifyZeroProof( pubKey *curves.Point, ciphertext *elgamal.Ciphertext, ) bool { + if proof == nil || proof.Yp == nil || proof.Yd == nil || proof.Z == nil || pubKey == nil || + ciphertext == nil || ciphertext.C == nil || ciphertext.D == nil { + return false + } + // Extract necessary values P := *pubKey C := ciphertext.C diff --git a/pkg/zkproofs/zero_balance_test.go b/pkg/zkproofs/zero_balance_test.go index ed438e3..b21478f 100644 --- a/pkg/zkproofs/zero_balance_test.go +++ b/pkg/zkproofs/zero_balance_test.go @@ -1,7 +1,9 @@ package zkproofs import ( + "crypto/rand" "encoding/json" + "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" "github.com/stretchr/testify/require" "testing" @@ -92,3 +94,184 @@ func TestZeroBalanceProof_MarshalUnmarshalJSON(t *testing.T) { require.True(t, original.Yd.Equal(unmarshaled.Yd), "Yd points should be equal") require.Equal(t, original.Z, unmarshaled.Z, "Z scalars should be equal") } + +func TestZeroBalanceProof_InvalidRandomness(t *testing.T) { + privateKey, err := elgamal.GenerateKey() + require.NoError(t, err, "Failed to generate private key") + + eg := elgamal.NewTwistedElgamal() + keypair, err := eg.KeyGen(*privateKey, TestDenom) + require.NoError(t, err, "Failed to generate key pair") + + ciphertext, _, err := eg.Encrypt(keypair.PublicKey, 0) + require.NoError(t, err, "Failed to encrypt amount") + + curve := curves.ED25519() + Yp := curve.Point.Neg() + Yd := curve.Point.Double() + Z := curve.Scalar.Zero() + + invalidProof := &ZeroBalanceProof{ + Yp: Yp, + Yd: Yd, + Z: Z, + } + + // Verify the proof + valid := VerifyZeroProof(invalidProof, &keypair.PublicKey, ciphertext) + require.False(t, valid, "Proof with invalid randomness fields should fail verification") +} + +func TestZeroBalanceProof_ExtremelyLargeScalars(t *testing.T) { + privateKey, err := elgamal.GenerateKey() + require.NoError(t, err, "Failed to generate private key") + + eg := elgamal.NewTwistedElgamal() + keypair, err := eg.KeyGen(*privateKey, TestDenom) + require.NoError(t, err, "Failed to generate key pair") + + ciphertext, _, err := eg.Encrypt(keypair.PublicKey, 0) + require.NoError(t, err, "Failed to encrypt amount") + + // Manually set Z to an extremely large scalar + largeScalarBytes := make([]byte, 64) // Adjust size based on curve's scalar size + _, err = rand.Read(largeScalarBytes) + require.NoError(t, err, "Failed to generate random bytes for large scalar") + + largeScalar, err := curves.ED25519().Scalar.SetBytesWide(largeScalarBytes) + require.NoError(t, err, "Failed to set large scalar") + + proof, err := NewZeroBalanceProof(keypair, ciphertext) + require.NoError(t, err, "Failed to generate proof") + + // Assign the large scalar to Z + proof.Z = largeScalar + + // Verify the proof + valid := VerifyZeroProof(proof, &keypair.PublicKey, ciphertext) + require.False(t, valid, "Proof with extremely large scalar Z should fail verification") +} + +func TestZeroBalanceProof_TamperedProof(t *testing.T) { + privateKey, err := elgamal.GenerateKey() + require.NoError(t, err, "Failed to generate private key") + + eg := elgamal.NewTwistedElgamal() + keypair, err := eg.KeyGen(*privateKey, TestDenom) + require.NoError(t, err, "Failed to generate key pair") + + ciphertext, _, err := eg.Encrypt(keypair.PublicKey, 0) + require.NoError(t, err, "Failed to encrypt amount") + + // Generate ZeroBalanceProof + proof, err := NewZeroBalanceProof(keypair, ciphertext) + require.NoError(t, err, "Failed to generate proof") + + // Marshal the proof to JSON + data, err := json.Marshal(proof) + require.NoError(t, err, "Marshaling should not produce an error") + + // Tamper with the JSON data (e.g., modify the 'z' field) + var tamperedData map[string]string + err = json.Unmarshal(data, &tamperedData) + require.NoError(t, err, "Unmarshaling into map should not produce an error") + + // Modify the 'z' field to an invalid value + tamperedData["Z"] = "tampered_value" + + // Marshal the tampered data back to JSON + tamperedJSON, err := json.Marshal(tamperedData) + require.NoError(t, err, "Marshaling tampered data should not produce an error") + + // Unmarshal the tampered JSON back to a ZeroBalanceProof + var tamperedProof ZeroBalanceProof + err = json.Unmarshal(tamperedJSON, &tamperedProof) + require.Error(t, err, "Unmarshaling tampered JSON should produce an error") +} + +// Invalid input: test cases +func TestZeroBalanceProof_InvalidInput(t *testing.T) { + _, err := NewZeroBalanceProof(nil, nil) + require.Error(t, err, "Should return an error when keypair is nil") + require.Contains(t, err.Error(), "keypair is invalid") + + _, err = NewZeroBalanceProof(&elgamal.KeyPair{}, nil) + require.Error(t, err, "Should return an error when ciphertext is nil") + require.Contains(t, err.Error(), "keypair is invalid") + + privateKey, err := elgamal.GenerateKey() + require.NoError(t, err, "Failed to generate private key") + + eg := elgamal.NewTwistedElgamal() + keypair, _ := eg.KeyGen(*privateKey, TestDenom) + _, err = NewZeroBalanceProof(&elgamal.KeyPair{PublicKey: keypair.PublicKey}, nil) + require.Error(t, err, "Should return an error when ciphertext is nil") + require.Contains(t, err.Error(), "keypair is invalid") + + // Test with nil ciphertext + _, err = NewZeroBalanceProof(keypair, nil) + require.Error(t, err, "Should return an error when ciphertext is nil") + require.Contains(t, err.Error(), "ciphertext is invalid") + + // Test with nil D in ciphertext + invalidCiphertext1 := &elgamal.Ciphertext{C: keypair.PublicKey, D: nil} + _, err = NewZeroBalanceProof(keypair, invalidCiphertext1) + require.Error(t, err, "Should return an error when ciphertext.D is nil") + require.Contains(t, err.Error(), "ciphertext is invalid") + + // Test with nil C in ciphertext + invalidCiphertext2 := &elgamal.Ciphertext{C: nil, D: keypair.PublicKey} + _, err = NewZeroBalanceProof(keypair, invalidCiphertext2) + require.Error(t, err, "Should return an error when ciphertext.C is nil") + require.Contains(t, err.Error(), "ciphertext is invalid") +} + +func TestVerifyZeroProof_InvalidInput(t *testing.T) { + privateKey, err := elgamal.GenerateKey() + require.NoError(t, err, "Failed to generate private key") + + eg := elgamal.NewTwistedElgamal() + keypair, err := eg.KeyGen(*privateKey, TestDenom) + require.NoError(t, err, "Failed to generate key pair") + + ciphertext, _, err := eg.Encrypt(keypair.PublicKey, 0) + require.NoError(t, err, "Failed to encrypt amount") + + proof, err := NewZeroBalanceProof(keypair, ciphertext) + require.NoError(t, err, "Failed to generate proof") + + // Test with nil proof + valid := VerifyZeroProof(nil, &keypair.PublicKey, ciphertext) + require.False(t, valid, "Verification should fail when proof is nil") + + nilFieldsProof := &ZeroBalanceProof{} + valid = VerifyZeroProof(nilFieldsProof, &keypair.PublicKey, ciphertext) + + // Test with nil public key + valid = VerifyZeroProof(proof, nil, ciphertext) + require.False(t, valid, "Verification should fail when public key is nil") + + // Test with nil ciphertext + valid = VerifyZeroProof(proof, &keypair.PublicKey, nil) + require.False(t, valid, "Verification should fail when ciphertext is nil") + + // Test with nil D in ciphertext + invalidCiphertext1 := &elgamal.Ciphertext{C: keypair.PublicKey, D: nil} + valid = VerifyZeroProof(proof, &keypair.PublicKey, invalidCiphertext1) + require.False(t, valid, "Verification should fail when ciphertext.D is nil") + + // Test with nil C in ciphertext + invalidCiphertext2 := &elgamal.Ciphertext{C: nil, D: keypair.PublicKey} + valid = VerifyZeroProof(proof, &keypair.PublicKey, invalidCiphertext2) + require.False(t, valid, "Verification should fail when ciphertext.C is nil") + + // Test with invalid proof values + curve := curves.ED25519() + invalidProof := &ZeroBalanceProof{ + Yp: curve.Point.Neg(), + Yd: curve.Point.Double(), + Z: curve.Scalar.Zero(), + } + valid = VerifyZeroProof(invalidProof, &keypair.PublicKey, ciphertext) + require.False(t, valid, "Verification should fail with invalid proof values") +} From c28a2852b49124fcecd7de6bdd078fd1f46c0834 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 14:17:53 -0700 Subject: [PATCH 12/30] - invalid inputs tests for NewCiphertextCiphertextEqualityProof --- .../ciphertext_ciphertext_equality.go | 41 ++++++++- .../ciphertext_ciphertext_equality_test.go | 83 +++++++++++++++++++ 2 files changed, 121 insertions(+), 3 deletions(-) diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality.go b/pkg/zkproofs/ciphertext_ciphertext_equality.go index 56057e5..6693b74 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality.go @@ -3,6 +3,7 @@ package zkproofs import ( "crypto/rand" "encoding/json" + "errors" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" ) @@ -24,21 +25,42 @@ type CiphertextCiphertextEqualityProof struct { // // Parameters: // - sourceKeypair: The ElGamal keypair associated with the first ciphertext to be proved. -// - destinationPubkey: The ElGamal pubkey associated with the second ElGamal ciphertext. +// - destinationPubKey: The ElGamal public key associated with the second ElGamal ciphertext. // - sourceCiphertext: The first ElGamal ciphertext for which the prover knows a decryption key. // - destinationOpening: The opening (randomness) associated with the second ElGamal ciphertext. // - amount: The message associated with the ElGamal ciphertext and Pedersen commitment. func NewCiphertextCiphertextEqualityProof( sourceKeypair *elgamal.KeyPair, - destinationPubkey *curves.Point, + destinationPubKey *curves.Point, sourceCiphertext *elgamal.Ciphertext, destinationOpening *curves.Scalar, amount *curves.Scalar, ) (*CiphertextCiphertextEqualityProof, error) { + // validate input + if sourceKeypair == nil || sourceKeypair.PublicKey == nil || sourceKeypair.PrivateKey == nil { + return nil, errors.New("keypair is invalid") + } + + if destinationPubKey == nil { + return nil, errors.New("destinationPubKey is invalid") + } + + if sourceCiphertext == nil || sourceCiphertext.C == nil || sourceCiphertext.D == nil { + return nil, errors.New("sourceCiphertext is invalid") + } + + if destinationOpening == nil { + return nil, errors.New("destinationOpening is invalid") + } + + if amount == nil { + return nil, errors.New("amount is invalid") + } + // Extract necessary values pSource := sourceKeypair.PublicKey dSource := sourceCiphertext.D - pDestination := *destinationPubkey + pDestination := *destinationPubKey s := sourceKeypair.PrivateKey x := *amount @@ -111,6 +133,19 @@ func VerifyCiphertextCiphertextEquality( sourceCiphertext *elgamal.Ciphertext, destinationCiphertext *elgamal.Ciphertext, ) bool { + // validate proof for nil values + if proof == nil || proof.Y0 == nil || proof.Y1 == nil || proof.Y2 == nil || proof.Y3 == nil || proof.Zs == nil || + proof.Zx == nil || proof.Zr == nil { + return false + } + + // validate other input + if sourcePubKey == nil || destinationPubKey == nil || + sourceCiphertext == nil || sourceCiphertext.C == nil || sourceCiphertext.D == nil || + destinationCiphertext == nil || destinationCiphertext.C == nil || destinationCiphertext.D == nil { + return false + } + // Extract necessary values pSource := *sourcePubKey cSource := sourceCiphertext.C diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go index b67d83c..9bbdf21 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go @@ -240,3 +240,86 @@ func TestCiphertextCiphertextEqualityProof_UnmarshalJSON_Valid(t *testing.T) { require.Equal(t, proof.Zx, unmarshaled.Zx, "Zx scalars should be equal after unmarshaling") require.Equal(t, proof.Zr, unmarshaled.Zr, "Zr scalars should be equal after unmarshaling") } + +// Invalid input tests for NewCiphertextCiphertextEqualityProof +func TestNewCiphertextCiphertextEqualityProof_InvalidInputs(t *testing.T) { + sourcePrivateKey, _ := elgamal.GenerateKey() + destPrivateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) + destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) + + amount := uint64(100) + + // Encrypt the amount using source and destination public keys + sourceCiphertext, _, _ := eg.Encrypt(sourceKeypair.PublicKey, amount) + _, destinationRandomness, _ := eg.Encrypt(destinationKeypair.PublicKey, amount) + scalarAmtValue := new(big.Int).SetUint64(amount) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) + + t.Run("Invalid Source Keypair", func(t *testing.T) { + // Source keypair is nil + proof, err := NewCiphertextCiphertextEqualityProof( + nil, + &destinationKeypair.PublicKey, + sourceCiphertext, + &destinationRandomness, + &scalarAmount, + ) + require.Error(t, err, "Proof generation should fail for nil source keypair") + require.Nil(t, proof, "Proof should be nil for nil source keypair") + }) + + t.Run("Invalid Destination Public Key", func(t *testing.T) { + // Destination public key is nil + proof, err := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + nil, + sourceCiphertext, + &destinationRandomness, + &scalarAmount, + ) + require.Error(t, err, "Proof generation should fail for nil destination public key") + require.Nil(t, proof, "Proof should be nil for nil destination public key") + }) + + t.Run("Invalid Source Ciphertext", func(t *testing.T) { + // Source ciphertext is nil + proof, err := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + &destinationKeypair.PublicKey, + nil, + &destinationRandomness, + &scalarAmount, + ) + require.Error(t, err, "Proof generation should fail for nil source ciphertext") + require.Nil(t, proof, "Proof should be nil") + }) + + t.Run("Invalid Destination Randomness", func(t *testing.T) { + // Destination randomness is nil + proof, err := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + &destinationKeypair.PublicKey, + sourceCiphertext, + nil, + &scalarAmount, + ) + require.Error(t, err, "Proof generation should fail for nil destination randomness") + require.Nil(t, proof, "Proof should be nil") + }) + + t.Run("Invalid Amount", func(t *testing.T) { + // Amount is nil + proof, err := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + &destinationKeypair.PublicKey, + sourceCiphertext, + &destinationRandomness, + nil, + ) + require.Error(t, err, "Proof generation should fail for nil amount") + require.Nil(t, proof, "Proof should be nil") + }) + +} From d6d75681b0475ec038a038a5aec65e1d5f76c66d Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 14:41:55 -0700 Subject: [PATCH 13/30] - invalid inputs tests for NewCiphertextCommitmentEqualityProof --- .../ciphertext_commitment_equality.go | 29 +++++++++ .../ciphertext_commitment_equality_test.go | 61 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/pkg/zkproofs/ciphertext_commitment_equality.go b/pkg/zkproofs/ciphertext_commitment_equality.go index 6e1cf19..1999d9b 100644 --- a/pkg/zkproofs/ciphertext_commitment_equality.go +++ b/pkg/zkproofs/ciphertext_commitment_equality.go @@ -3,6 +3,7 @@ package zkproofs import ( "crypto/rand" "encoding/json" + "errors" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" ) @@ -25,6 +26,23 @@ func NewCiphertextCommitmentEqualityProof( pedersenOpening *curves.Scalar, amount *curves.Scalar, ) (*CiphertextCommitmentEqualityProof, error) { + // Validate input + if sourceKeypair == nil || sourceKeypair.PublicKey == nil || sourceKeypair.PrivateKey == nil { + return nil, errors.New("keypair is invalid") + } + + if sourceCiphertext == nil || sourceCiphertext.D == nil || sourceCiphertext.C == nil { + return nil, errors.New("sourceCiphertext is invalid") + } + + if pedersenOpening == nil { + return nil, errors.New("pedersenOpening is invalid") + } + + if amount == nil { + return nil, errors.New("amount is invalid") + } + // Extract necessary values P := sourceKeypair.PublicKey // Public key in twisted ElGamal aesgcm scheme D := sourceCiphertext.D // D part of the twisted ElGamal ciphertext @@ -90,6 +108,17 @@ func VerifyCiphertextCommitmentEquality( sourceCiphertext *elgamal.Ciphertext, pedersenCommitment *curves.Point, ) bool { + // Validate proof + if proof == nil || proof.Y0 == nil || proof.Y1 == nil || proof.Y2 == nil || proof.Zs == nil || proof.Zx == nil || + proof.Zr == nil { + return false + } + + // Validate input + if sourcePubKey == nil || sourceCiphertext == nil || pedersenCommitment == nil { + return false + } + // Extract necessary values P := *sourcePubKey D := sourceCiphertext.D diff --git a/pkg/zkproofs/ciphertext_commitment_equality_test.go b/pkg/zkproofs/ciphertext_commitment_equality_test.go index 4bffa2d..52ed2c6 100644 --- a/pkg/zkproofs/ciphertext_commitment_equality_test.go +++ b/pkg/zkproofs/ciphertext_commitment_equality_test.go @@ -157,3 +157,64 @@ func TestCiphertextCommitmentEqualityProof_MarshalUnmarshalJSON(t *testing.T) { require.Equal(t, proof.Zx, unmarshaled.Zx, "Zx scalars should be equal") require.Equal(t, proof.Zr, unmarshaled.Zr, "Zr scalars should be equal") } + +func TestNewCiphertextCommitmentEqualityProof_InvalidInput(t *testing.T) { + sourcePrivateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) + + amount := uint64(100) + + // Encrypt the amount using source and destination public keys + sourceCiphertext, sourceRandomness, _ := eg.Encrypt(sourceKeypair.PublicKey, amount) + scalarAmtValue := new(big.Int).SetUint64(amount) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) + + t.Run("Invalid Source Keypair", func(t *testing.T) { + // Source keypair is nil + proof, err := NewCiphertextCommitmentEqualityProof( + nil, + sourceCiphertext, + &sourceRandomness, + &scalarAmount, + ) + require.Error(t, err, "Proof generation should fail for nil source keypair") + require.Nil(t, proof, "Proof should be nil for nil source keypair") + }) + + t.Run("Invalid Source Ciphertext", func(t *testing.T) { + // Source ciphertext is nil + proof, err := NewCiphertextCommitmentEqualityProof( + sourceKeypair, + nil, + &sourceRandomness, + &scalarAmount, + ) + require.Error(t, err, "Proof generation should fail for nil source ciphertext") + require.Nil(t, proof, "Proof should be nil for nil source ciphertext") + }) + + t.Run("Invalid Source Pedersen opening", func(t *testing.T) { + // Source Pedersen Opening is nil + proof, err := NewCiphertextCommitmentEqualityProof( + sourceKeypair, + sourceCiphertext, + nil, + &scalarAmount, + ) + require.Error(t, err, "Proof generation should fail for nil Pedersen opening") + require.Nil(t, proof, "Proof should be nil") + }) + + t.Run("Invalid Amount", func(t *testing.T) { + // Amount is nil + proof, err := NewCiphertextCommitmentEqualityProof( + sourceKeypair, + sourceCiphertext, + &sourceRandomness, + nil, + ) + require.Error(t, err, "Proof generation should fail for nil amount") + require.Nil(t, proof, "Proof should be nil") + }) +} From 3b0ef00b73dfade66d3d93bbc4c90ef4f170cc31 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 14:46:22 -0700 Subject: [PATCH 14/30] - more input validation tests --- .../ciphertext_ciphertext_equality_test.go | 97 +++++++++++++++++++ .../ciphertext_commitment_equality_test.go | 76 +++++++++++++++ 2 files changed, 173 insertions(+) diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go index 9bbdf21..1975357 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go @@ -323,3 +323,100 @@ func TestNewCiphertextCiphertextEqualityProof_InvalidInputs(t *testing.T) { }) } + +// Invalid input tests for VerifyCiphertextCiphertextEquality +func TestVerifyCiphertextCiphertextEquality_InvalidInputs(t *testing.T) { + sourcePrivateKey, _ := elgamal.GenerateKey() + destPrivateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) + destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) + + amount := uint64(100) + + // Encrypt the amount using source and destination public keys + sourceCiphertext, _, _ := eg.Encrypt(sourceKeypair.PublicKey, amount) + _, destinationRandomness, _ := eg.Encrypt(destinationKeypair.PublicKey, amount) + scalarAmtValue := new(big.Int).SetUint64(amount) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) + + proof, _ := NewCiphertextCiphertextEqualityProof( + sourceKeypair, + &destinationKeypair.PublicKey, + sourceCiphertext, + &destinationRandomness, + &scalarAmount, + ) + + t.Run("Invalid Proof", func(t *testing.T) { + // Proof is nil + valid := VerifyCiphertextCiphertextEquality( + nil, + &sourceKeypair.PublicKey, + &destinationKeypair.PublicKey, + sourceCiphertext, + sourceCiphertext, + ) + require.False(t, valid, "Proof verification should fail for nil proof") + }) + + t.Run("Invalid Proof with nil fields", func(t *testing.T) { + // Proof is nil + valid := VerifyCiphertextCiphertextEquality( + &CiphertextCiphertextEqualityProof{}, + &sourceKeypair.PublicKey, + &destinationKeypair.PublicKey, + sourceCiphertext, + sourceCiphertext, + ) + require.False(t, valid, "Proof verification should fail for nil proof") + }) + + t.Run("Invalid Source Public Key", func(t *testing.T) { + // Source public key is nil + valid := VerifyCiphertextCiphertextEquality( + proof, + nil, + &destinationKeypair.PublicKey, + sourceCiphertext, + sourceCiphertext, + ) + require.False(t, valid, "Proof verification should fail for nil source public key") + }) + + t.Run("Invalid Destination Public Key", func(t *testing.T) { + // Destination public key is nil + valid := VerifyCiphertextCiphertextEquality( + proof, + &sourceKeypair.PublicKey, + nil, + sourceCiphertext, + sourceCiphertext, + ) + require.False(t, valid, "Proof verification should fail for nil destination public key") + }) + + t.Run("Invalid Source Ciphertext", func(t *testing.T) { + // Source ciphertext is nil + valid := VerifyCiphertextCiphertextEquality( + proof, + &sourceKeypair.PublicKey, + &destinationKeypair.PublicKey, + nil, + sourceCiphertext, + ) + require.False(t, valid, "Proof verification should fail for nil source ciphertext") + }) + + t.Run("Invalid Destination Ciphertext", func(t *testing.T) { + // Destination ciphertext is nil + valid := VerifyCiphertextCiphertextEquality( + proof, + &sourceKeypair.PublicKey, + &destinationKeypair.PublicKey, + sourceCiphertext, + nil, + ) + require.False(t, valid, "Proof verification should fail for nil destination ciphertext") + }) +} diff --git a/pkg/zkproofs/ciphertext_commitment_equality_test.go b/pkg/zkproofs/ciphertext_commitment_equality_test.go index 52ed2c6..e98eef8 100644 --- a/pkg/zkproofs/ciphertext_commitment_equality_test.go +++ b/pkg/zkproofs/ciphertext_commitment_equality_test.go @@ -218,3 +218,79 @@ func TestNewCiphertextCommitmentEqualityProof_InvalidInput(t *testing.T) { require.Nil(t, proof, "Proof should be nil") }) } + +func TestVerifyCiphertextCommitmentEquality_InvalidInput(t *testing.T) { + sourcePrivateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) + + amount := uint64(100) + + // Encrypt the amount using source and destination public keys + sourceCiphertext, sourceRandomness, _ := eg.Encrypt(sourceKeypair.PublicKey, amount) + scalarAmtValue := new(big.Int).SetUint64(amount) + scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) + + // Generate the proof + proof, _ := NewCiphertextCommitmentEqualityProof( + sourceKeypair, + sourceCiphertext, + &sourceRandomness, + &scalarAmount, + ) + + t.Run("Invalid Proof", func(t *testing.T) { + // Proof is nil + valid := VerifyCiphertextCommitmentEquality( + nil, + &sourceKeypair.PublicKey, + sourceCiphertext, + &sourceCiphertext.C, + ) + require.False(t, valid, "Proof verification should fail for nil proof") + }) + + t.Run("Invalid Proof With nil fields", func(t *testing.T) { + // Proof is nil + valid := VerifyCiphertextCommitmentEquality( + &CiphertextCommitmentEqualityProof{}, + &sourceKeypair.PublicKey, + sourceCiphertext, + &sourceCiphertext.C, + ) + require.False(t, valid, "Proof verification should fail for proof with nil fields") + }) + + t.Run("Invalid Source Public Key", func(t *testing.T) { + // Source public key is nil + valid := VerifyCiphertextCommitmentEquality( + proof, + nil, + sourceCiphertext, + &sourceCiphertext.C, + ) + require.False(t, valid, "Proof verification should fail for nil source public key") + }) + + t.Run("Invalid Source Ciphertext", func(t *testing.T) { + // Source ciphertext is nil + valid := VerifyCiphertextCommitmentEquality( + proof, + &sourceKeypair.PublicKey, + nil, + &sourceCiphertext.C, + ) + require.False(t, valid, "Proof verification should fail for nil source ciphertext") + }) + + t.Run("Invalid Pedersen Commitment", func(t *testing.T) { + // Pedersen commitment is nil + valid := VerifyCiphertextCommitmentEquality( + proof, + &sourceKeypair.PublicKey, + sourceCiphertext, + nil, + ) + require.False(t, valid, "Proof verification should fail for nil Pedersen commitment") + }) +} From ba3ce73be2895cd11083869d462b1dc32e06f87c Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 15:05:32 -0700 Subject: [PATCH 15/30] - input validation for NewCiphertextValidityProof --- pkg/zkproofs/ciphertext_validity.go | 35 +++++++++---- pkg/zkproofs/ciphertext_validity_test.go | 65 +++++++++++++++++++++--- 2 files changed, 82 insertions(+), 18 deletions(-) diff --git a/pkg/zkproofs/ciphertext_validity.go b/pkg/zkproofs/ciphertext_validity.go index e96e424..e5d23ac 100644 --- a/pkg/zkproofs/ciphertext_validity.go +++ b/pkg/zkproofs/ciphertext_validity.go @@ -3,6 +3,7 @@ package zkproofs import ( crand "crypto/rand" "encoding/json" + "errors" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" "math/big" @@ -18,10 +19,22 @@ type CiphertextValidityProof struct { } // NewCiphertextValidityProof generates a zero-knowledge proof that a ciphertext is properly encrypted. -func NewCiphertextValidityProof(message uint64, r *curves.Scalar, pubKey curves.Point, ct *elgamal.Ciphertext) *CiphertextValidityProof { +func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgamal.Ciphertext, message uint64) (*CiphertextValidityProof, error) { + // Validate input + if r == nil { + return nil, errors.New("invalid randomness factor") + } + + if pubKey == nil { + return nil, errors.New("invalid public key") + } + + if ct == nil || ct.C == nil || ct.D == nil { + return nil, errors.New("invalid ciphertext") + } + eg := elgamal.NewTwistedElgamal() - var proof CiphertextValidityProof H := eg.GetH() G := eg.GetG() @@ -42,7 +55,8 @@ func NewCiphertextValidityProof(message uint64, r *curves.Scalar, pubKey curves. Commitment2 := pubKey.Mul(rBlind) // Commitment2 = rBlind * P // Step 3: Generate a challenge using the Fiat-Shamir heuristic. - // The challenge is basically just a hash of all the provided values. This locks in the values and makes sure that the proof cannot be for some other set of values. + // The challenge is basically just a hash of all the provided values. This locks in the values and makes sure that + // the proof cannot be for some other set of values. hashData := append(Commitment1.ToAffineCompressed(), Commitment2.ToAffineCompressed()...) hashData = append(hashData, ct.C.ToAffineCompressed()...) hashData = append(hashData, ct.D.ToAffineCompressed()...) @@ -52,14 +66,13 @@ func NewCiphertextValidityProof(message uint64, r *curves.Scalar, pubKey curves. Response1 := challenge.MulAdd(*r, rBlind) // Response1 = rBlind + challenge * r Response2 := challenge.MulAdd(x, xBlind) // Response2 = xBlind + challenge * x - // Store the proof - proof.Commitment1 = Commitment1 - proof.Commitment2 = Commitment2 - proof.Challenge = challenge - proof.Response1 = Response1 - proof.Response2 = Response2 - - return &proof + return &CiphertextValidityProof{ + Commitment1: Commitment1, + Commitment2: Commitment2, + Challenge: challenge, + Response1: Response1, + Response2: Response2, + }, nil } // VerifyCiphertextValidityProof verifies the zero-knowledge proof that a ciphertext is valid. diff --git a/pkg/zkproofs/ciphertext_validity_test.go b/pkg/zkproofs/ciphertext_validity_test.go index 4f80f0a..566b0a1 100644 --- a/pkg/zkproofs/ciphertext_validity_test.go +++ b/pkg/zkproofs/ciphertext_validity_test.go @@ -11,7 +11,7 @@ func TestValidityProof(t *testing.T) { privateKey, err := elgamal.GenerateKey() altPrivateKey, err := elgamal.GenerateKey() - require.Nil(t, err, "Should not have error here") + require.Nil(t, err) eg := elgamal.NewTwistedElgamal() keys, err := eg.KeyGen(*privateKey, TestDenom) @@ -19,9 +19,10 @@ func TestValidityProof(t *testing.T) { message12 := uint64(12) ciphertext12, randomness12, err := eg.Encrypt(keys.PublicKey, message12) - require.Nil(t, err, "Should not have error here") + require.Nil(t, err) - proof12 := NewCiphertextValidityProof(message12, &randomness12, keys.PublicKey, ciphertext12) + proof12, err := NewCiphertextValidityProof(&randomness12, keys.PublicKey, ciphertext12, message12) + require.Nil(t, err) validated := VerifyCiphertextValidityProof(proof12, keys.PublicKey, ciphertext12) require.True(t, validated, "Validating with the correct parameters should validate as true") @@ -37,7 +38,8 @@ func TestValidityProof(t *testing.T) { require.False(t, validated, "Validating with the wrong ciphertext should validate as false") // Generate proof using the wrong pubkey - wrongProof := NewCiphertextValidityProof(message12, &randomness12, altKeys.PublicKey, ciphertext12) + wrongProof, err := NewCiphertextValidityProof(&randomness12, altKeys.PublicKey, ciphertext12, message12) + require.Nil(t, err) validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) require.False(t, validated, "Proof generated with the wrong PublicKey should validate as false") @@ -46,12 +48,14 @@ func TestValidityProof(t *testing.T) { " when using the same pubkey to validate") // Generate proof using the wrong randomness - wrongProof = NewCiphertextValidityProof(message12, &randomness42, keys.PublicKey, ciphertext12) + wrongProof, err = NewCiphertextValidityProof(&randomness42, keys.PublicKey, ciphertext12, message12) + require.Nil(t, err) validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) require.False(t, validated, "Proof generated with the wrong randomness should validate as false") // Generate proof with the wrong ciphertext - wrongProof = NewCiphertextValidityProof(message42, &randomness42, keys.PublicKey, ciphertext12) + wrongProof, err = NewCiphertextValidityProof(&randomness42, keys.PublicKey, ciphertext12, message42) + require.Nil(t, err) validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) require.False(t, validated, "Proof generated with the wrong ciphertext should validate as false") } @@ -64,7 +68,8 @@ func TestCiphertextValidityProof_MarshalUnmarshalJSON(t *testing.T) { message12 := uint64(12) ciphertext12, randomness12, _ := eg.Encrypt(keys.PublicKey, message12) - original := NewCiphertextValidityProof(message12, &randomness12, keys.PublicKey, ciphertext12) + original, err := NewCiphertextValidityProof(&randomness12, keys.PublicKey, ciphertext12, message12) + require.NoError(t, err, "Proof generation should not produce an error") // Marshal the proof to JSON data, err := json.Marshal(original) require.NoError(t, err, "Marshaling should not produce an error") @@ -81,3 +86,49 @@ func TestCiphertextValidityProof_MarshalUnmarshalJSON(t *testing.T) { require.Equal(t, original.Response1, unmarshaled.Response1, "Response1 scalars should be equal") require.Equal(t, original.Response2, unmarshaled.Response2, "Response2 scalars should be equal") } + +func TestNewCiphertextValidityProof_InvalidInput(t *testing.T) { + privateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + keys, _ := eg.KeyGen(*privateKey, TestDenom) + + amount := uint64(100) + // Encrypt the amount using source and destination public keys + ciphertext, randomness, _ := eg.Encrypt(keys.PublicKey, amount) + + t.Run("Invalid Source Randomness", func(t *testing.T) { + // Source randomness is nil + proof, err := NewCiphertextValidityProof( + nil, + keys.PublicKey, + ciphertext, + amount, + ) + require.Nil(t, proof, "Proof should be nil for nil source randomness") + require.Error(t, err, "Proof generation should fail for nil source randomness") + }) + + t.Run("Invalid Source PublicKey", func(t *testing.T) { + // Source public key is nil + proof, err := NewCiphertextValidityProof( + &randomness, + nil, + ciphertext, + amount, + ) + require.Nil(t, proof, "Proof should be nil for nil source public key") + require.Error(t, err, "Proof generation should fail for nil source public key") + }) + + t.Run("Invalid Source Ciphertext", func(t *testing.T) { + // Source ciphertext is nil + proof, err := NewCiphertextValidityProof( + &randomness, + keys.PublicKey, + nil, + amount, + ) + require.Nil(t, proof, "Proof should be nil for nil source ciphertext") + require.Error(t, err, "Proof generation should fail for nil source ciphertext") + }) +} From 1c4fcde974e64dcc8bf0a725b3cff2721f2fc9b7 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 15:11:14 -0700 Subject: [PATCH 16/30] - input validation for VerifyCiphertextValidityProof --- pkg/zkproofs/ciphertext_validity.go | 5 ++++ pkg/zkproofs/ciphertext_validity_test.go | 35 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/pkg/zkproofs/ciphertext_validity.go b/pkg/zkproofs/ciphertext_validity.go index e5d23ac..04468dc 100644 --- a/pkg/zkproofs/ciphertext_validity.go +++ b/pkg/zkproofs/ciphertext_validity.go @@ -77,6 +77,11 @@ func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgam // VerifyCiphertextValidityProof verifies the zero-knowledge proof that a ciphertext is valid. func VerifyCiphertextValidityProof(proof *CiphertextValidityProof, pubKey curves.Point, ct *elgamal.Ciphertext) bool { + // Validate input + if proof == nil || proof.Commitment1 == nil || proof.Commitment2 == nil || proof.Challenge == nil || + proof.Response1 == nil || proof.Response2 == nil || pubKey == nil || ct == nil || ct.C == nil || ct.D == nil { + return false + } eg := elgamal.NewTwistedElgamal() H := eg.GetH() diff --git a/pkg/zkproofs/ciphertext_validity_test.go b/pkg/zkproofs/ciphertext_validity_test.go index 566b0a1..f66eecc 100644 --- a/pkg/zkproofs/ciphertext_validity_test.go +++ b/pkg/zkproofs/ciphertext_validity_test.go @@ -132,3 +132,38 @@ func TestNewCiphertextValidityProof_InvalidInput(t *testing.T) { require.Error(t, err, "Proof generation should fail for nil source ciphertext") }) } + +func TestVerifyCiphertextValidityProof_Invalid_Input(t *testing.T) { + privateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + keys, _ := eg.KeyGen(*privateKey, TestDenom) + + amount := uint64(100) + // Encrypt the amount using source and destination public keys + ciphertext, randomness, _ := eg.Encrypt(keys.PublicKey, amount) + + proof, _ := NewCiphertextValidityProof(&randomness, keys.PublicKey, ciphertext, amount) + + t.Run("Invalid (nil) proof", func(t *testing.T) { + // Proof commitment1 is nil + validated := VerifyCiphertextValidityProof(nil, keys.PublicKey, ciphertext) + require.False(t, validated, "Validation should fail for nil commitment1") + }) + + t.Run("Invalid proof with nil fields", func(t *testing.T) { + validated := VerifyCiphertextValidityProof(&CiphertextValidityProof{}, keys.PublicKey, ciphertext) + require.False(t, validated, "Validation should fail for proof with nil fields") + }) + + t.Run("Invalid Public Key", func(t *testing.T) { + // Proof challenge is nil + validated := VerifyCiphertextValidityProof(proof, nil, ciphertext) + require.False(t, validated, "Validation should fail for nil challenge") + }) + + t.Run("Invalid Ciphertext", func(t *testing.T) { + // Proof response1 is nil + validated := VerifyCiphertextValidityProof(proof, keys.PublicKey, nil) + require.False(t, validated, "Validation should fail for nil response1") + }) +} From 9d1da5195c041ae77c0ef6248ec61ecbee3b179b Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 15:33:17 -0700 Subject: [PATCH 17/30] - input validation for NewPubKeyValidityProof & VerifyPubKeyValidityProof --- pkg/zkproofs/pubkey_validity.go | 19 +++++++++-- pkg/zkproofs/pubkey_validity_test.go | 48 ++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/pkg/zkproofs/pubkey_validity.go b/pkg/zkproofs/pubkey_validity.go index 01128cb..0debccb 100644 --- a/pkg/zkproofs/pubkey_validity.go +++ b/pkg/zkproofs/pubkey_validity.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "crypto/sha256" "encoding/json" + "errors" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" ) @@ -21,7 +22,16 @@ type PubKeyValidityProof struct { // NewPubKeyValidityProof generates the sigma protocol proof // The proof here is that the creator of this PubKey also knows the corresponding PrivateKey -func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) *PubKeyValidityProof { +func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) (*PubKeyValidityProof, error) { + // Validate input + if pubKey == nil { + return nil, errors.New("invalid public key") + } + + if privKey == nil { + return nil, errors.New("invalid private key") + } + eg := elgamal.NewTwistedElgamal() H := eg.GetH() // Prover generates a random scalar y @@ -42,11 +52,16 @@ func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) *PubKeyV return &PubKeyValidityProof{ Y: Y, Z: z, - } + }, nil } // VerifyPubKeyValidityProof verifies the validity of the proof func VerifyPubKeyValidityProof(pubKey curves.Point, proof PubKeyValidityProof) bool { + // Validate input + if pubKey == nil || proof.Y == nil || proof.Z == nil { + return false + } + eg := elgamal.NewTwistedElgamal() H := eg.GetH() // Recompute the challenge c diff --git a/pkg/zkproofs/pubkey_validity_test.go b/pkg/zkproofs/pubkey_validity_test.go index ff8c2d4..cf463bc 100644 --- a/pkg/zkproofs/pubkey_validity_test.go +++ b/pkg/zkproofs/pubkey_validity_test.go @@ -10,16 +10,16 @@ import ( func TestPubKeyValidityProof(t *testing.T) { privateKey, err := elgamal.GenerateKey() altPrivateKey, err := elgamal.GenerateKey() - - require.Nil(t, err, "Should not have error here") + require.Nil(t, err) eg := elgamal.NewTwistedElgamal() keys, err := eg.KeyGen(*privateKey, TestDenom) altKeys, err := eg.KeyGen(*altPrivateKey, TestDenom) - require.Nil(t, err, "Should not have error here") + require.Nil(t, err) // Prove knowledge of the private key - proof := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) + proof, err := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) + require.Nil(t, err) // Verify the proof valid := VerifyPubKeyValidityProof(keys.PublicKey, *proof) @@ -29,7 +29,8 @@ func TestPubKeyValidityProof(t *testing.T) { require.False(t, invalid, "Proof should be invalid when trying to validate wrong PublicKey") // Generate proof with the wrong private key. - badProof := NewPubKeyValidityProof(keys.PublicKey, altKeys.PrivateKey) + badProof, err := NewPubKeyValidityProof(keys.PublicKey, altKeys.PrivateKey) + require.Nil(t, err) invalid = VerifyPubKeyValidityProof(keys.PublicKey, *badProof) require.False(t, invalid, "Proof generated with wrong Privkey should be validated as false.") } @@ -39,7 +40,8 @@ func TestPubKeyValidityProof_MarshalUnmarshalJSON(t *testing.T) { eg := elgamal.NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, TestDenom) - original := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) + original, err := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) + require.NoError(t, err, "Proof generation should not produce an error") // Marshal the proof to JSON data, err := json.Marshal(original) require.NoError(t, err, "Marshaling should not produce an error") @@ -53,3 +55,37 @@ func TestPubKeyValidityProof_MarshalUnmarshalJSON(t *testing.T) { require.True(t, original.Y.Equal(unmarshaled.Y), "Y points should be equal") require.Equal(t, original.Z, unmarshaled.Z, "Z scalars should be equal") } + +func TestNewPubKeyValidityProof_InvalidInput(t *testing.T) { + privateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + keys, _ := eg.KeyGen(*privateKey, TestDenom) + + _, err := NewPubKeyValidityProof(nil, keys.PrivateKey) + require.Error(t, err, "Generating proof with nil public key should produce an error") + + _, err = NewPubKeyValidityProof(keys.PublicKey, nil) + require.Error(t, err, "Generating proof with nil private key should produce an error") +} + +func TestVerifyPubKeyValidityProof_InvalidInput(t *testing.T) { + privateKey, err := elgamal.GenerateKey() + require.Nil(t, err) + + eg := elgamal.NewTwistedElgamal() + keys, err := eg.KeyGen(*privateKey, TestDenom) + require.Nil(t, err) + + // Prove knowledge of the private key + proof, err := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) + require.Nil(t, err) + + // Verify the proof + valid := VerifyPubKeyValidityProof(nil, *proof) + require.False(t, valid, "proof verification should fail for nil public key") + + invalidProof := PubKeyValidityProof{} + + valid = VerifyPubKeyValidityProof(keys.PublicKey, invalidProof) + require.False(t, valid, "proof verification should fail for invalid proof") +} From b116f1b27448465b9a5c80c505a6c7d14d478def Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Fri, 11 Oct 2024 15:49:45 -0700 Subject: [PATCH 18/30] - input validation for NewRangeProof & VerifyRangeProof --- pkg/zkproofs/range.go | 15 +++++++- pkg/zkproofs/range_test.go | 71 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/pkg/zkproofs/range.go b/pkg/zkproofs/range.go index 6762ac0..2daf31e 100644 --- a/pkg/zkproofs/range.go +++ b/pkg/zkproofs/range.go @@ -3,6 +3,7 @@ package zkproofs import ( crand "crypto/rand" "encoding/json" + "errors" "github.com/coinbase/kryptology/pkg/bulletproof" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/gtank/merlin" @@ -22,6 +23,9 @@ type RangeProof struct { // Note that while this proof generator takes in the plaintext of the value, it will only work in conjunction with a // ciphertext that encrypts value using the given randomness. func NewRangeProof(upperBound, value int, randomness curves.Scalar) (*RangeProof, error) { + if randomness == nil { + return nil, errors.New("invalid randomness factor") + } curve := curves.ED25519() prover, err := bulletproof.NewRangeProver(upperBound, getRangeDomain(), getIppDomain(), *curve) if err != nil { @@ -47,7 +51,16 @@ func NewRangeProof(upperBound, value int, randomness curves.Scalar) (*RangeProof } // VerifyRangeProof verifies the range proof for the given ciphertext -func VerifyRangeProof(proof *RangeProof, ciphertext elgamal.Ciphertext) (bool, error) { +func VerifyRangeProof(proof *RangeProof, ciphertext *elgamal.Ciphertext) (bool, error) { + // Validate input + if proof == nil || proof.Proof == nil || proof.Randomness == nil { + return false, errors.New("invalid proof") + } + + if ciphertext == nil || ciphertext.C == nil || ciphertext.D == nil { + return false, errors.New("invalid ciphertext") + } + curve := curves.ED25519() // Verifier gets the proof, the commitment, the generators to verify the value is within the range verifier, err := bulletproof.NewRangeVerifier(proof.UpperBound, getRangeDomain(), getIppDomain(), *curve) diff --git a/pkg/zkproofs/range_test.go b/pkg/zkproofs/range_test.go index c457d8f..7dd4a96 100644 --- a/pkg/zkproofs/range_test.go +++ b/pkg/zkproofs/range_test.go @@ -154,7 +154,7 @@ func TestRangeProofs(t *testing.T) { proof, err := NewRangeProof(n, value, gamma) require.Nil(t, err) - verified, err := VerifyRangeProof(proof, *ciphertext) + verified, err := VerifyRangeProof(proof, ciphertext) require.NoError(t, err) require.True(t, verified) @@ -162,7 +162,7 @@ func TestRangeProofs(t *testing.T) { ciphertext101, _, err := eg.Encrypt(keyPair.PublicKey, uint64(101)) require.Nil(t, err) - verified, err = VerifyRangeProof(proof, *ciphertext101) + verified, err = VerifyRangeProof(proof, ciphertext101) require.Error(t, err) require.False(t, verified) } @@ -194,7 +194,72 @@ func TestRangeProofsWithMarshaling(t *testing.T) { err = json.Unmarshal(marshaledProof, &unmarshaled) require.NoError(t, err, "Unmarshaling should not produce an error") - verified, err := VerifyRangeProof(&unmarshaled, *ciphertext) + verified, err := VerifyRangeProof(&unmarshaled, ciphertext) require.NoError(t, err) require.True(t, verified) } + +func TestRangeProofs_InvalidInput(t *testing.T) { + privateKey, err := elgamal.GenerateKey() + require.Nil(t, err, "Error generating private key") + + eg := elgamal.NewTwistedElgamal() + keyPair, err := eg.KeyGen(*privateKey, TestDenom) + require.Nil(t, err, "Error generating key pair") + + _, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(10)) + + t.Run("Invalid upper bound", func(t *testing.T) { + // Proof is nil + _, err := NewRangeProof( + 0, 0, gamma, + ) + require.EqualError(t, err, "range NewRangeProver: getGeneratorPoints splitPointVector: length of points must be at least one") + }) + + t.Run("Invalid randomness factor", func(t *testing.T) { + // Proof is nil + _, err := NewRangeProof( + 1, 0, nil, + ) + require.EqualError(t, err, "invalid randomness factor") + }) +} + +func TestVerifyRangeProof_InvalidInput(t *testing.T) { + privateKey, _ := elgamal.GenerateKey() + eg := elgamal.NewTwistedElgamal() + keyPair, _ := eg.KeyGen(*privateKey, TestDenom) + ciphertext, gamma, _ := eg.Encrypt(keyPair.PublicKey, uint64(10)) + + proof, err := NewRangeProof(64, 10, gamma) + require.NoError(t, err) + + t.Run("Nil proof", func(t *testing.T) { + // Proof is nil + valid, err := VerifyRangeProof(nil, ciphertext) + require.EqualError(t, err, "invalid proof") + require.False(t, valid) + }) + + t.Run("Proof with nil fields", func(t *testing.T) { + valid, err := VerifyRangeProof(&RangeProof{}, ciphertext) + require.EqualError(t, err, "invalid proof") + require.False(t, valid) + }) + + t.Run("nil ciphertext", func(t *testing.T) { + // Proof is nil + valid, err := VerifyRangeProof(proof, nil) + require.EqualError(t, err, "invalid ciphertext") + require.False(t, valid) + }) + + t.Run("Ciphertext with nil fields", func(t *testing.T) { + // Proof is nil + valid, err := VerifyRangeProof(proof, &elgamal.Ciphertext{}) + require.EqualError(t, err, "invalid ciphertext") + require.False(t, valid) + }) + +} From 83c64dcef4a41efab4dfc02dfbac2159dd89fa6d Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Mon, 14 Oct 2024 11:00:50 -0700 Subject: [PATCH 19/30] - fix linting errors --- .../ciphertext_ciphertext_equality.go | 6 +--- .../ciphertext_ciphertext_equality_test.go | 36 +++++++++---------- .../ciphertext_commitment_equality.go | 5 +-- .../ciphertext_commitment_equality_test.go | 6 ++-- pkg/zkproofs/ciphertext_validity_test.go | 10 +++--- pkg/zkproofs/pubkey_validity_test.go | 12 +++---- pkg/zkproofs/range_test.go | 10 +++--- pkg/zkproofs/zero_balance.go | 5 +-- pkg/zkproofs/zero_balance_test.go | 10 +++--- 9 files changed, 45 insertions(+), 55 deletions(-) diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality.go b/pkg/zkproofs/ciphertext_ciphertext_equality.go index 6693b74..7fe9f6b 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality.go @@ -210,11 +210,7 @@ func VerifyCiphertextCiphertextEquality( rhsY3 := proof.Y3.Add(cDd) - if !lhsY3.Equal(rhsY3) { - return false - } - - return true + return lhsY3.Equal(rhsY3) } // MarshalJSON for CiphertextCiphertextEqualityProof diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go index 1975357..fddd84b 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go @@ -53,11 +53,11 @@ func TestCiphertextCiphertextEqualityProof(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Key generation - sourcePrivateKey, err := elgamal.GenerateKey() - destPrivateKey, err := elgamal.GenerateKey() + sourcePrivateKey, _ := elgamal.GenerateKey() + destPrivateKey, _ := elgamal.GenerateKey() eg := elgamal.NewTwistedElgamal() - sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) - destinationKeypair, err := eg.KeyGen(*destPrivateKey, TestDenom) + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) + destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) var actualDestinationPubkey *curves.Point if tt.useDifferentPublicKey { @@ -111,11 +111,11 @@ func TestCiphertextCiphertextEqualityProof(t *testing.T) { func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { t.Run("Zero Amounts", func(t *testing.T) { // Key generation - sourcePrivateKey, err := elgamal.GenerateKey() - destPrivateKey, err := elgamal.GenerateKey() + sourcePrivateKey, _ := elgamal.GenerateKey() + destPrivateKey, _ := elgamal.GenerateKey() eg := elgamal.NewTwistedElgamal() - sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) - destinationKeypair, err := eg.KeyGen(*destPrivateKey, TestDenom) + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) + destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) amount := uint64(0) @@ -152,12 +152,12 @@ func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { t.Run("Maximum Amount", func(t *testing.T) { // Key generation - sourcePrivateKey, err := elgamal.GenerateKey() - destPrivateKey, err := elgamal.GenerateKey() + sourcePrivateKey, _ := elgamal.GenerateKey() + destPrivateKey, _ := elgamal.GenerateKey() eg := elgamal.NewTwistedElgamal() - sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) - destinationKeypair, err := eg.KeyGen(*destPrivateKey, TestDenom) + destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) amount := uint64(1 << 60) // A large amount to test scalability @@ -195,17 +195,17 @@ func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { } func TestCiphertextCiphertextEqualityProof_UnmarshalJSON_Valid(t *testing.T) { - sourcePrivateKey, err := elgamal.GenerateKey() - destPrivateKey, err := elgamal.GenerateKey() + sourcePrivateKey, _ := elgamal.GenerateKey() + destPrivateKey, _ := elgamal.GenerateKey() eg := elgamal.NewTwistedElgamal() - sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) - destinationKeypair, err := eg.KeyGen(*destPrivateKey, TestDenom) + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) + destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) amount := uint64(100) // Encrypt the amount using source and destination public keys - sourceCiphertext, _, err := eg.Encrypt(sourceKeypair.PublicKey, amount) - _, destinationRandomness, err := eg.Encrypt(destinationKeypair.PublicKey, amount) + sourceCiphertext, _, _ := eg.Encrypt(sourceKeypair.PublicKey, amount) + _, destinationRandomness, _ := eg.Encrypt(destinationKeypair.PublicKey, amount) scalarAmtValue := new(big.Int).SetUint64(amount) scalarAmount, _ := curves.ED25519().Scalar.SetBigInt(scalarAmtValue) diff --git a/pkg/zkproofs/ciphertext_commitment_equality.go b/pkg/zkproofs/ciphertext_commitment_equality.go index 1999d9b..28b3bd7 100644 --- a/pkg/zkproofs/ciphertext_commitment_equality.go +++ b/pkg/zkproofs/ciphertext_commitment_equality.go @@ -162,11 +162,8 @@ func VerifyCiphertextCommitmentEquality( lhsY2 := zxG2.Add(zrH) // zx * G + zr * H cCPed := cPed.Mul(c) // c * cPed rhsY2 := cCPed.Add(proof.Y2) // c * cPed + Y2 - if !lhsY2.Equal(rhsY2) { - return false - } - return true + return lhsY2.Equal(rhsY2) } // MarshalJSON for CiphertextCommitmentEqualityProof diff --git a/pkg/zkproofs/ciphertext_commitment_equality_test.go b/pkg/zkproofs/ciphertext_commitment_equality_test.go index e98eef8..28e5c7a 100644 --- a/pkg/zkproofs/ciphertext_commitment_equality_test.go +++ b/pkg/zkproofs/ciphertext_commitment_equality_test.go @@ -52,9 +52,9 @@ func TestCiphertextCommitmentEqualityProof(t *testing.T) { tt := tt // Capture range variable t.Run(tt.name, func(t *testing.T) { // Key generation - sourcePrivateKey, err := elgamal.GenerateKey() + sourcePrivateKey, _ := elgamal.GenerateKey() eg := elgamal.NewTwistedElgamal() - sourceKeypair, err := eg.KeyGen(*sourcePrivateKey, TestDenom) + sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) // Encrypt the source amount sourceCiphertext, sourceRandomness, err := eg.Encrypt(sourceKeypair.PublicKey, tt.sourceAmount) @@ -140,6 +140,8 @@ func TestCiphertextCommitmentEqualityProof_MarshalUnmarshalJSON(t *testing.T) { &scalarAmount, ) + require.NoError(t, err, "Proof generation should not fail") + // Marshal the proof to JSON data, err := json.Marshal(proof) require.NoError(t, err, "Marshaling should not produce an error") diff --git a/pkg/zkproofs/ciphertext_validity_test.go b/pkg/zkproofs/ciphertext_validity_test.go index f66eecc..0f25ba4 100644 --- a/pkg/zkproofs/ciphertext_validity_test.go +++ b/pkg/zkproofs/ciphertext_validity_test.go @@ -8,14 +8,12 @@ import ( ) func TestValidityProof(t *testing.T) { - privateKey, err := elgamal.GenerateKey() - altPrivateKey, err := elgamal.GenerateKey() - - require.Nil(t, err) + privateKey, _ := elgamal.GenerateKey() + altPrivateKey, _ := elgamal.GenerateKey() eg := elgamal.NewTwistedElgamal() - keys, err := eg.KeyGen(*privateKey, TestDenom) - altKeys, err := eg.KeyGen(*altPrivateKey, TestDenom) + keys, _ := eg.KeyGen(*privateKey, TestDenom) + altKeys, _ := eg.KeyGen(*altPrivateKey, TestDenom) message12 := uint64(12) ciphertext12, randomness12, err := eg.Encrypt(keys.PublicKey, message12) diff --git a/pkg/zkproofs/pubkey_validity_test.go b/pkg/zkproofs/pubkey_validity_test.go index cf463bc..535dd2e 100644 --- a/pkg/zkproofs/pubkey_validity_test.go +++ b/pkg/zkproofs/pubkey_validity_test.go @@ -8,18 +8,16 @@ import ( ) func TestPubKeyValidityProof(t *testing.T) { - privateKey, err := elgamal.GenerateKey() - altPrivateKey, err := elgamal.GenerateKey() - require.Nil(t, err) + privateKey, _ := elgamal.GenerateKey() + altPrivateKey, _ := elgamal.GenerateKey() eg := elgamal.NewTwistedElgamal() - keys, err := eg.KeyGen(*privateKey, TestDenom) - altKeys, err := eg.KeyGen(*altPrivateKey, TestDenom) - require.Nil(t, err) + keys, _ := eg.KeyGen(*privateKey, TestDenom) + altKeys, _ := eg.KeyGen(*altPrivateKey, TestDenom) // Prove knowledge of the private key proof, err := NewPubKeyValidityProof(keys.PublicKey, keys.PrivateKey) - require.Nil(t, err) + require.NoError(t, err) // Verify the proof valid := VerifyPubKeyValidityProof(keys.PublicKey, *proof) diff --git a/pkg/zkproofs/range_test.go b/pkg/zkproofs/range_test.go index 7dd4a96..eebba64 100644 --- a/pkg/zkproofs/range_test.go +++ b/pkg/zkproofs/range_test.go @@ -25,7 +25,7 @@ func TestValueIsInRange(t *testing.T) { keyPair, err := eg.KeyGen(*privateKey, TestDenom) require.Nil(t, err, "Error generating key pair") - ciphertext, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(value)) + ciphertext, gamma, _ := eg.Encrypt(keyPair.PublicKey, uint64(value)) prover, err := bulletproof.NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) require.NoError(t, err) @@ -64,7 +64,7 @@ func TestRangeAttacksAreInfeasible(t *testing.T) { keyPair, err := eg.KeyGen(*privateKey, TestDenom) require.Nil(t, err, "Error generating key pair") - ciphertext, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(value)) + ciphertext, gamma, _ := eg.Encrypt(keyPair.PublicKey, uint64(value)) prover, err := bulletproof.NewRangeProver(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) require.NoError(t, err) @@ -149,7 +149,7 @@ func TestRangeProofs(t *testing.T) { keyPair, err := eg.KeyGen(*privateKey, TestDenom) require.Nil(t, err, "Error generating key pair") - ciphertext, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(value)) + ciphertext, gamma, _ := eg.Encrypt(keyPair.PublicKey, uint64(value)) proof, err := NewRangeProof(n, value, gamma) require.Nil(t, err) @@ -180,7 +180,7 @@ func TestRangeProofsWithMarshaling(t *testing.T) { keyPair, err := eg.KeyGen(*privateKey, TestDenom) require.Nil(t, err, "Error generating key pair") - ciphertext, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(value)) + ciphertext, gamma, _ := eg.Encrypt(keyPair.PublicKey, uint64(value)) proof, err := NewRangeProof(n, value, gamma) require.Nil(t, err) @@ -207,7 +207,7 @@ func TestRangeProofs_InvalidInput(t *testing.T) { keyPair, err := eg.KeyGen(*privateKey, TestDenom) require.Nil(t, err, "Error generating key pair") - _, gamma, err := eg.Encrypt(keyPair.PublicKey, uint64(10)) + _, gamma, _ := eg.Encrypt(keyPair.PublicKey, uint64(10)) t.Run("Invalid upper bound", func(t *testing.T) { // Proof is nil diff --git a/pkg/zkproofs/zero_balance.go b/pkg/zkproofs/zero_balance.go index d7f0449..711e757 100644 --- a/pkg/zkproofs/zero_balance.go +++ b/pkg/zkproofs/zero_balance.go @@ -99,10 +99,7 @@ func VerifyZeroProof( cC := C.Mul(c) // c * C rhsYd := cC.Add(proof.Yd) // c * C + Yd - if !lhsYd.Equal(rhsYd) { - return false - } - return true + return lhsYd.Equal(rhsYd) } // MarshalJSON for ZeroBalanceProof diff --git a/pkg/zkproofs/zero_balance_test.go b/pkg/zkproofs/zero_balance_test.go index b21478f..070ee2d 100644 --- a/pkg/zkproofs/zero_balance_test.go +++ b/pkg/zkproofs/zero_balance_test.go @@ -39,12 +39,12 @@ func TestZeroBalanceProof(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Setup keypair - privateKey, err := elgamal.GenerateKey() - altPrivateKey, err := elgamal.GenerateKey() + privateKey, _ := elgamal.GenerateKey() + altPrivateKey, _ := elgamal.GenerateKey() eg := elgamal.NewTwistedElgamal() - keypair, err := eg.KeyGen(*privateKey, TestDenom) - alternativeKeypair, err := eg.KeyGen(*altPrivateKey, TestDenom) + keypair, _ := eg.KeyGen(*privateKey, TestDenom) + alternativeKeypair, _ := eg.KeyGen(*altPrivateKey, TestDenom) actualPublicKey := keypair.PublicKey if tt.useDifferentPubKey { @@ -79,6 +79,7 @@ func TestZeroBalanceProof_MarshalUnmarshalJSON(t *testing.T) { ciphertext, _, _ := eg.Encrypt(keypair.PublicKey, 0) original, err := NewZeroBalanceProof(keypair, ciphertext) + require.NoError(t, err, "Proof generation should not produce an error") // Marshal the proof to JSON data, err := json.Marshal(original) @@ -246,6 +247,7 @@ func TestVerifyZeroProof_InvalidInput(t *testing.T) { nilFieldsProof := &ZeroBalanceProof{} valid = VerifyZeroProof(nilFieldsProof, &keypair.PublicKey, ciphertext) + require.False(t, valid, "Verification should fail when proof has nil fields") // Test with nil public key valid = VerifyZeroProof(proof, nil, ciphertext) From acb5f415e976a6d05fd09b410bc778330ae3c3d4 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Mon, 14 Oct 2024 11:30:05 -0700 Subject: [PATCH 20/30] - fix workflow --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a3ed287..9a769c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -61,12 +61,12 @@ jobs: - name: Determine packages to test (excluding elgamal) id: packages run: | - PACKAGES=$(go list ./... | grep -v '/pkg/encryption/elgamal') + PACKAGES=$(go list ./... | grep -v '/pkg/encryption/elgamal$') echo "PACKAGES=$PACKAGES" >> $GITHUB_ENV - name: Run general unit tests run: | - go test -mod=readonly -race -v -timeout 5m $PACKAGES + go test -mod=readonly -race -v -timeout 5m ${PACKAGES} # Define a matrix for ElGamal package test subsets elgamal-tests: From 9edaaa04eb22f7d4d5010ea084ebe18018af64dd Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Mon, 14 Oct 2024 14:08:17 -0700 Subject: [PATCH 21/30] - fix workflow - 2 --- .github/workflows/test.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9a769c4..101e60b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -58,15 +58,10 @@ jobs: ~/.cache/go-build key: ${{ needs.setup.outputs.cache-key }} - - name: Determine packages to test (excluding elgamal) - id: packages + - name: Run general unit tests (excluding elgamal) run: | - PACKAGES=$(go list ./... | grep -v '/pkg/encryption/elgamal$') - echo "PACKAGES=$PACKAGES" >> $GITHUB_ENV - - - name: Run general unit tests - run: | - go test -mod=readonly -race -v -timeout 5m ${PACKAGES} + echo "Determining packages to test (excluding elgamal)..." + go list ./... | grep -v '/pkg/encryption/elgamal$' | xargs go test -mod=readonly -race -v -timeout 5m # Define a matrix for ElGamal package test subsets elgamal-tests: From 3ac0ad7e03112999995ac69518d0763b878c750a Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Tue, 15 Oct 2024 16:38:58 -0700 Subject: [PATCH 22/30] Switch to github.com/coinbase/kryptology sei fork --- go.mod | 12 +++--- go.sum | 77 ++++++++------------------------------ pkg/zkproofs/range.go | 7 ++-- pkg/zkproofs/range_test.go | 22 +++++++---- 4 files changed, 41 insertions(+), 77 deletions(-) diff --git a/go.mod b/go.mod index 1b02239..6261f16 100644 --- a/go.mod +++ b/go.mod @@ -5,21 +5,23 @@ go 1.21 require ( github.com/bwesterb/go-ristretto v1.2.3 github.com/coinbase/kryptology v1.8.0 - github.com/ethereum/go-ethereum v1.10.26 + github.com/ethereum/go-ethereum v1.13.15 github.com/gtank/merlin v0.1.1 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.27.0 ) require ( - filippo.io/edwards25519 v1.0.0-rc.1 // indirect - github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401 // indirect - github.com/consensys/gnark-crypto v0.5.3 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/pretty v0.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.25.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/coinbase/kryptology => github.com/sei-protocol/coinbase-kryptology v0.0.0-20241015231206-08f61b7965cd diff --git a/go.sum b/go.sum index 28328f4..b9a4c91 100644 --- a/go.sum +++ b/go.sum @@ -1,90 +1,45 @@ filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401 h1:0tjUthKCaF8zwF9Qg7lfnep0xdo4n8WiFUfQPaMHX6g= -github.com/btcsuite/btcd v0.21.0-beta.0.20201114000516-e9c7a5ac6401/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/coinbase/kryptology v1.8.0 h1:Aoq4gdTsJhSU3lNWsD5BWmFSz2pE0GlmrljaOxepdYY= -github.com/coinbase/kryptology v1.8.0/go.mod h1:RYXOAPdzOGUe3qlSFkMGn58i3xUA8hmxYHksuq+8ciI= -github.com/consensys/bavard v0.1.8-0.20210915155054-088da2f7f54a/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.5.3 h1:4xLFGZR3NWEH2zy+YzvzHicpToQR8FXFbfLNvpGB+rE= -github.com/consensys/gnark-crypto v0.5.3/go.mod h1:hOdPlWQV1gDLp7faZVeg8Y0iEPFaOUnCc4XeCCk96p0= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= -github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= +github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/sei-protocol/coinbase-kryptology v0.0.0-20241015231206-08f61b7965cd h1:R/g4pa6pgegLAAt1NTrO1qVJ3uZH9hfcMcc4yLz1cgg= +github.com/sei-protocol/coinbase-kryptology v0.0.0-20241015231206-08f61b7965cd/go.mod h1:vAKKp7/qgfMtPXMseamOlZMqK7BytjfOm0rFKWph5c4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/pkg/zkproofs/range.go b/pkg/zkproofs/range.go index 2daf31e..06fedee 100644 --- a/pkg/zkproofs/range.go +++ b/pkg/zkproofs/range.go @@ -36,10 +36,11 @@ func NewRangeProof(upperBound, value int, randomness curves.Scalar) (*RangeProof g := eg.GetG() h := eg.GetH() u := curve.Point.Random(crand.Reader) + proofGenerators := bulletproof.NewRangeProofGenerators(g, h, u) transcript := getTranscript() vScalar := curve.Scalar.New(value) - proof, err := prover.Prove(vScalar, randomness, upperBound, g, h, u, transcript) + proof, err := prover.Prove(vScalar, randomness, upperBound, proofGenerators, transcript) if err != nil { return nil, err } @@ -71,8 +72,8 @@ func VerifyRangeProof(proof *RangeProof, ciphertext *elgamal.Ciphertext) (bool, eg := elgamal.NewTwistedElgamal() g := eg.GetG() h := eg.GetH() - - verified, err := verifier.Verify(proof.Proof, ciphertext.C, g, h, proof.Randomness, proof.UpperBound, getTranscript()) + proofGenerators := bulletproof.NewRangeProofGenerators(g, h, proof.Randomness) + verified, err := verifier.Verify(proof.Proof, ciphertext.C, proofGenerators, proof.UpperBound, getTranscript()) if err != nil { return false, err } diff --git a/pkg/zkproofs/range_test.go b/pkg/zkproofs/range_test.go index eebba64..9640ce0 100644 --- a/pkg/zkproofs/range_test.go +++ b/pkg/zkproofs/range_test.go @@ -32,8 +32,10 @@ func TestValueIsInRange(t *testing.T) { g := eg.GetG() h := eg.GetH() u := curve.Point.Random(crand.Reader) + + proofGenerators := bulletproof.NewRangeProofGenerators(g, h, u) transcript := merlin.NewTranscript("test") - proof, err := prover.Prove(v, gamma, n, g, h, u, transcript) + proof, err := prover.Prove(v, gamma, n, proofGenerators, transcript) require.NoError(t, err) // Verifier gets the proof, the commitment, the generators to verify the value is within the range @@ -41,13 +43,13 @@ func TestValueIsInRange(t *testing.T) { require.NoError(t, err) transcriptVerifier := merlin.NewTranscript("test") - verified, err := verifier.Verify(proof, ciphertext.C, g, h, u, n, transcriptVerifier) + verified, err := verifier.Verify(proof, ciphertext.C, proofGenerators, n, transcriptVerifier) require.NoError(t, err) require.True(t, verified) ciphertext101, _, err := eg.Encrypt(keyPair.PublicKey, uint64(101)) require.Nil(t, err) - verified, err = verifier.Verify(proof, ciphertext101.C, g, h, u, n, transcriptVerifier) + verified, err = verifier.Verify(proof, ciphertext101.C, proofGenerators, n, transcriptVerifier) require.Error(t, err) require.False(t, verified) } @@ -71,8 +73,10 @@ func TestRangeAttacksAreInfeasible(t *testing.T) { g := eg.GetG() h := eg.GetH() u := curve.Point.Random(crand.Reader) + proofGenerators := bulletproof.NewRangeProofGenerators(g, h, u) transcript := merlin.NewTranscript("test") - proof, err := prover.Prove(v, gamma, n, g, h, u, transcript) + + proof, err := prover.Prove(v, gamma, n, proofGenerators, transcript) require.NoError(t, err) // for 90 to 110 generate ciphertexts and see if we can guess the encrypted value @@ -84,7 +88,7 @@ func TestRangeAttacksAreInfeasible(t *testing.T) { verifier, e := bulletproof.NewRangeVerifier(n, []byte("rangeDomain"), []byte("ippDomain"), *curve) require.NoError(t, e) - verified, _ := verifier.Verify(proof, ct.C, g, h, u, n, transcriptVerifier) + verified, _ := verifier.Verify(proof, ct.C, proofGenerators, n, transcriptVerifier) if verified { t.Errorf("Attack successful: the number is %d", i) } @@ -101,7 +105,7 @@ func TestRangeAttacksAreInfeasible(t *testing.T) { transcriptVerifier := merlin.NewTranscript("test") require.NoError(t, e) - verified, _ := verifier.Verify(proof, ciphertext.C, g, h, u, i, transcriptVerifier) + verified, _ := verifier.Verify(proof, ciphertext.C, proofGenerators, i, transcriptVerifier) if verified { t.Errorf("Attack successful: the number is in the range 2^%d", i) } @@ -118,8 +122,9 @@ func TestRangeVerifyNotInRange(t *testing.T) { g := curve.Point.Random(crand.Reader) h := curve.Point.Random(crand.Reader) u := curve.Point.Random(crand.Reader) + proofGenerators := bulletproof.NewRangeProofGenerators(g, h, u) transcript := merlin.NewTranscript("test") - _, err = prover.Prove(v, gamma, n, g, h, u, transcript) + _, err = prover.Prove(v, gamma, n, proofGenerators, transcript) require.Error(t, err) } @@ -133,8 +138,9 @@ func TestRangeVerifyNotInRangeNegativeValue(t *testing.T) { g := curve.Point.Random(crand.Reader) h := curve.Point.Random(crand.Reader) u := curve.Point.Random(crand.Reader) + proofGenerators := bulletproof.NewRangeProofGenerators(g, h, u) transcript := merlin.NewTranscript("test") - _, err = prover.Prove(v, gamma, n, g, h, u, transcript) + _, err = prover.Prove(v, gamma, n, proofGenerators, transcript) require.Error(t, err) } From 8472431b95dd8ad4b3c6ca40b474f23d817b342b Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 17 Oct 2024 14:45:55 -0700 Subject: [PATCH 23/30] Addressing comments - part 1 --- .../ciphertext_ciphertext_equality_test.go | 2 +- .../ciphertext_commitment_equality.go | 10 ++++ pkg/zkproofs/ciphertext_validity.go | 46 +++++++++++-------- pkg/zkproofs/ciphertext_validity_test.go | 1 - 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go index fddd84b..62a9f64 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go @@ -35,7 +35,7 @@ func TestCiphertextCiphertextEqualityProof(t *testing.T) { expectValid: false, }, { - name: "Invalid Proof - Different Public Keys", + name: "Invalid Proof - Wrong Dest Public Key", sourceAmount: 200, destinationAmount: 200, useDifferentPublicKey: true, diff --git a/pkg/zkproofs/ciphertext_commitment_equality.go b/pkg/zkproofs/ciphertext_commitment_equality.go index 28b3bd7..1b81b47 100644 --- a/pkg/zkproofs/ciphertext_commitment_equality.go +++ b/pkg/zkproofs/ciphertext_commitment_equality.go @@ -20,6 +20,11 @@ type CiphertextCommitmentEqualityProof struct { } // NewCiphertextCommitmentEqualityProof generates a new equality proof between a ciphertext and a Pedersen commitment. +// Parameters: +// - sourceKeypair: The ElGamal keypair associated with the ciphertext to be proved. +// - sourceCiphertext: The ElGamal ciphertext for which the prover knows a decryption key. +// - pedersenOpening: The opening (randomness) associated with the Pedersen commitment. +// - amount: The message associated with the ElGamal ciphertext and Pedersen commitment. func NewCiphertextCommitmentEqualityProof( sourceKeypair *elgamal.KeyPair, sourceCiphertext *elgamal.Ciphertext, @@ -102,6 +107,11 @@ func NewCiphertextCommitmentEqualityProof( // VerifyCiphertextCommitmentEquality verifies the zero-knowledge equality proof between a ciphertext and a Pedersen // commitment. +// Parameters: +// - proof: The proof to be verified. +// - sourcePubKey: The public key associated with the ciphertext to be proved. +// - sourceCiphertext: The ElGamal ciphertext for which the prover knows a decryption key. +// - pedersenCommitment: The Pedersen commitment to be proved. func VerifyCiphertextCommitmentEquality( proof *CiphertextCommitmentEqualityProof, sourcePubKey *curves.Point, diff --git a/pkg/zkproofs/ciphertext_validity.go b/pkg/zkproofs/ciphertext_validity.go index 04468dc..8a0fa31 100644 --- a/pkg/zkproofs/ciphertext_validity.go +++ b/pkg/zkproofs/ciphertext_validity.go @@ -13,12 +13,16 @@ import ( type CiphertextValidityProof struct { Commitment1 curves.Point Commitment2 curves.Point - Challenge curves.Scalar Response1 curves.Scalar Response2 curves.Scalar } -// NewCiphertextValidityProof generates a zero-knowledge proof that a ciphertext is properly encrypted. +// NewCiphertextValidityProof generates a zero-knowledge proof that the given ciphertext is properly encrypted using the given Public Key. +// Parameters: +// - r: The randomness factor used in the encryption. +// - pubKey: The public key used in the encryption. +// - ct: The ciphertext to prove the validity of. +// - message: The message that was encrypted in the ciphertext. func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgamal.Ciphertext, message uint64) (*CiphertextValidityProof, error) { // Validate input if r == nil { @@ -57,10 +61,10 @@ func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgam // Step 3: Generate a challenge using the Fiat-Shamir heuristic. // The challenge is basically just a hash of all the provided values. This locks in the values and makes sure that // the proof cannot be for some other set of values. - hashData := append(Commitment1.ToAffineCompressed(), Commitment2.ToAffineCompressed()...) - hashData = append(hashData, ct.C.ToAffineCompressed()...) - hashData = append(hashData, ct.D.ToAffineCompressed()...) - challenge := ed25519.Scalar.Hash(hashData) + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("C1", Commitment1.ToAffineCompressed()) + transcript.AppendMessage("C2", Commitment2.ToAffineCompressed()) + challenge := transcript.ChallengeScalar() // Step 4: Generate responses Response1 := challenge.MulAdd(*r, rBlind) // Response1 = rBlind + challenge * r @@ -69,17 +73,20 @@ func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgam return &CiphertextValidityProof{ Commitment1: Commitment1, Commitment2: Commitment2, - Challenge: challenge, Response1: Response1, Response2: Response2, }, nil } // VerifyCiphertextValidityProof verifies the zero-knowledge proof that a ciphertext is valid. +// Parameters: +// - proof: The proof to verify. +// - pubKey: The public key used in the encryption. +// - ct: The ciphertext to prove the validity of. func VerifyCiphertextValidityProof(proof *CiphertextValidityProof, pubKey curves.Point, ct *elgamal.Ciphertext) bool { // Validate input - if proof == nil || proof.Commitment1 == nil || proof.Commitment2 == nil || proof.Challenge == nil || - proof.Response1 == nil || proof.Response2 == nil || pubKey == nil || ct == nil || ct.C == nil || ct.D == nil { + if proof == nil || proof.Commitment1 == nil || proof.Commitment2 == nil || proof.Response1 == nil || + proof.Response2 == nil || pubKey == nil || ct == nil || ct.C == nil || ct.D == nil { return false } @@ -87,14 +94,20 @@ func VerifyCiphertextValidityProof(proof *CiphertextValidityProof, pubKey curves H := eg.GetH() G := eg.GetG() + // Step 0: Recompute the challenge using the Fiat-Shamir heuristic. + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("C1", proof.Commitment1.ToAffineCompressed()) + transcript.AppendMessage("C2", proof.Commitment2.ToAffineCompressed()) + challenge := transcript.ChallengeScalar() + // Step 1: Recompute Commitment1 - response1H := H.Mul(proof.Response1) // response1 * H - response2G := G.Mul(proof.Response2) // response2 * G - challengeC := ct.C.Mul(proof.Challenge) // challenge * C + response1H := H.Mul(proof.Response1) // response1 * H + response2G := G.Mul(proof.Response2) // response2 * G + challengeC := ct.C.Mul(challenge) // challenge * C recomputedCommitment1 := response1H.Add(response2G).Sub(challengeC) // Step 2: Recompute Commitment2 - challengeD := ct.D.Mul(proof.Challenge) // challenge * D + challengeD := ct.D.Mul(challenge) // challenge * D recomputedCommitment2 := pubKey.Mul(proof.Response1).Sub(challengeD) // Step 3: Check if the recomputed commitments match the original commitments @@ -107,7 +120,6 @@ func (p *CiphertextValidityProof) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]interface{}{ "commitment1": p.Commitment1.ToAffineCompressed(), // Assuming ToAffineCompressed returns a byte slice "commitment2": p.Commitment2.ToAffineCompressed(), - "challenge": p.Challenge.Bytes(), // Serialize Scalar to bytes "response1": p.Response1.Bytes(), // Serialize Scalar to bytes "response2": p.Response2.Bytes(), // Serialize Scalar to bytes }) @@ -119,7 +131,6 @@ func (p *CiphertextValidityProof) UnmarshalJSON(data []byte) error { var temp struct { Commitment1 []byte `json:"commitment1"` Commitment2 []byte `json:"commitment2"` - Challenge []byte `json:"challenge"` Response1 []byte `json:"response1"` Response2 []byte `json:"response2"` } @@ -139,10 +150,6 @@ func (p *CiphertextValidityProof) UnmarshalJSON(data []byte) error { if err != nil { return err } - challenge, err := ed25519.Scalar.SetBytes(temp.Challenge) - if err != nil { - return err - } response1, err := ed25519.Scalar.SetBytes(temp.Response1) if err != nil { return err @@ -155,7 +162,6 @@ func (p *CiphertextValidityProof) UnmarshalJSON(data []byte) error { // Assign the decoded values to the CiphertextValidityProof struct p.Commitment1 = commitment1 p.Commitment2 = commitment2 - p.Challenge = challenge p.Response1 = response1 p.Response2 = response2 diff --git a/pkg/zkproofs/ciphertext_validity_test.go b/pkg/zkproofs/ciphertext_validity_test.go index 0f25ba4..e7615cd 100644 --- a/pkg/zkproofs/ciphertext_validity_test.go +++ b/pkg/zkproofs/ciphertext_validity_test.go @@ -80,7 +80,6 @@ func TestCiphertextValidityProof_MarshalUnmarshalJSON(t *testing.T) { // Compare the original and unmarshaled proof require.True(t, original.Commitment1.Equal(unmarshaled.Commitment1), "Commitment1 points should be equal") require.True(t, original.Commitment2.Equal(unmarshaled.Commitment2), "Commitment2 points should be equal") - require.Equal(t, original.Challenge, unmarshaled.Challenge, "Challenge scalars should be equal") require.Equal(t, original.Response1, unmarshaled.Response1, "Response1 scalars should be equal") require.Equal(t, original.Response2, unmarshaled.Response2, "Response2 scalars should be equal") } From 695054e8d7a813a76ffb6c267f3e3142f708b472 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 17 Oct 2024 14:52:00 -0700 Subject: [PATCH 24/30] Addressing comments - part 2 --- pkg/zkproofs/ciphertext_validity.go | 30 ++++++++++++------------ pkg/zkproofs/ciphertext_validity_test.go | 22 ++++++++--------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/pkg/zkproofs/ciphertext_validity.go b/pkg/zkproofs/ciphertext_validity.go index 8a0fa31..1f683fa 100644 --- a/pkg/zkproofs/ciphertext_validity.go +++ b/pkg/zkproofs/ciphertext_validity.go @@ -19,13 +19,13 @@ type CiphertextValidityProof struct { // NewCiphertextValidityProof generates a zero-knowledge proof that the given ciphertext is properly encrypted using the given Public Key. // Parameters: -// - r: The randomness factor used in the encryption. +// - pedersenOpening: The randomness factor used in the encrypting the Ciphertext. // - pubKey: The public key used in the encryption. -// - ct: The ciphertext to prove the validity of. +// - ciphertext: The ciphertext to prove the validity of. // - message: The message that was encrypted in the ciphertext. -func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgamal.Ciphertext, message uint64) (*CiphertextValidityProof, error) { +func NewCiphertextValidityProof(pedersenOpening *curves.Scalar, pubKey curves.Point, ciphertext *elgamal.Ciphertext, message uint64) (*CiphertextValidityProof, error) { // Validate input - if r == nil { + if pedersenOpening == nil { return nil, errors.New("invalid randomness factor") } @@ -33,7 +33,7 @@ func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgam return nil, errors.New("invalid public key") } - if ct == nil || ct.C == nil || ct.D == nil { + if ciphertext == nil || ciphertext.C == nil || ciphertext.D == nil { return nil, errors.New("invalid ciphertext") } @@ -67,8 +67,8 @@ func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgam challenge := transcript.ChallengeScalar() // Step 4: Generate responses - Response1 := challenge.MulAdd(*r, rBlind) // Response1 = rBlind + challenge * r - Response2 := challenge.MulAdd(x, xBlind) // Response2 = xBlind + challenge * x + Response1 := challenge.MulAdd(*pedersenOpening, rBlind) // Response1 = rBlind + challenge * pedersenOpening + Response2 := challenge.MulAdd(x, xBlind) // Response2 = xBlind + challenge * x return &CiphertextValidityProof{ Commitment1: Commitment1, @@ -78,15 +78,15 @@ func NewCiphertextValidityProof(r *curves.Scalar, pubKey curves.Point, ct *elgam }, nil } -// VerifyCiphertextValidityProof verifies the zero-knowledge proof that a ciphertext is valid. +// VerifyCiphertextValidity verifies the zero-knowledge proof that a ciphertext is valid. // Parameters: // - proof: The proof to verify. // - pubKey: The public key used in the encryption. -// - ct: The ciphertext to prove the validity of. -func VerifyCiphertextValidityProof(proof *CiphertextValidityProof, pubKey curves.Point, ct *elgamal.Ciphertext) bool { +// - ciphertext: The ciphertext to prove the validity of. +func VerifyCiphertextValidity(proof *CiphertextValidityProof, pubKey curves.Point, ciphertext *elgamal.Ciphertext) bool { // Validate input if proof == nil || proof.Commitment1 == nil || proof.Commitment2 == nil || proof.Response1 == nil || - proof.Response2 == nil || pubKey == nil || ct == nil || ct.C == nil || ct.D == nil { + proof.Response2 == nil || pubKey == nil || ciphertext == nil || ciphertext.C == nil || ciphertext.D == nil { return false } @@ -101,13 +101,13 @@ func VerifyCiphertextValidityProof(proof *CiphertextValidityProof, pubKey curves challenge := transcript.ChallengeScalar() // Step 1: Recompute Commitment1 - response1H := H.Mul(proof.Response1) // response1 * H - response2G := G.Mul(proof.Response2) // response2 * G - challengeC := ct.C.Mul(challenge) // challenge * C + response1H := H.Mul(proof.Response1) // response1 * H + response2G := G.Mul(proof.Response2) // response2 * G + challengeC := ciphertext.C.Mul(challenge) // challenge * C recomputedCommitment1 := response1H.Add(response2G).Sub(challengeC) // Step 2: Recompute Commitment2 - challengeD := ct.D.Mul(challenge) // challenge * D + challengeD := ciphertext.D.Mul(challenge) // challenge * D recomputedCommitment2 := pubKey.Mul(proof.Response1).Sub(challengeD) // Step 3: Check if the recomputed commitments match the original commitments diff --git a/pkg/zkproofs/ciphertext_validity_test.go b/pkg/zkproofs/ciphertext_validity_test.go index e7615cd..02d4423 100644 --- a/pkg/zkproofs/ciphertext_validity_test.go +++ b/pkg/zkproofs/ciphertext_validity_test.go @@ -22,39 +22,39 @@ func TestValidityProof(t *testing.T) { proof12, err := NewCiphertextValidityProof(&randomness12, keys.PublicKey, ciphertext12, message12) require.Nil(t, err) - validated := VerifyCiphertextValidityProof(proof12, keys.PublicKey, ciphertext12) + validated := VerifyCiphertextValidity(proof12, keys.PublicKey, ciphertext12) require.True(t, validated, "Validating with the correct parameters should validate as true") - validated = VerifyCiphertextValidityProof(proof12, altKeys.PublicKey, ciphertext12) + validated = VerifyCiphertextValidity(proof12, altKeys.PublicKey, ciphertext12) require.False(t, validated, "Validating with the wrong PublicKey should validate as false") // Encrypt a message (e.g., x = 42) message42 := uint64(42) ciphertext42, randomness42, _ := eg.Encrypt(keys.PublicKey, message42) - validated = VerifyCiphertextValidityProof(proof12, altKeys.PublicKey, ciphertext42) + validated = VerifyCiphertextValidity(proof12, altKeys.PublicKey, ciphertext42) require.False(t, validated, "Validating with the wrong ciphertext should validate as false") // Generate proof using the wrong pubkey wrongProof, err := NewCiphertextValidityProof(&randomness12, altKeys.PublicKey, ciphertext12, message12) require.Nil(t, err) - validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) + validated = VerifyCiphertextValidity(wrongProof, keys.PublicKey, ciphertext12) require.False(t, validated, "Proof generated with the wrong PublicKey should validate as false") - validated = VerifyCiphertextValidityProof(wrongProof, altKeys.PublicKey, ciphertext12) + validated = VerifyCiphertextValidity(wrongProof, altKeys.PublicKey, ciphertext12) require.False(t, validated, "Proof generated with the wrong PublicKey should validate as false even"+ " when using the same pubkey to validate") // Generate proof using the wrong randomness wrongProof, err = NewCiphertextValidityProof(&randomness42, keys.PublicKey, ciphertext12, message12) require.Nil(t, err) - validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) + validated = VerifyCiphertextValidity(wrongProof, keys.PublicKey, ciphertext12) require.False(t, validated, "Proof generated with the wrong randomness should validate as false") // Generate proof with the wrong ciphertext wrongProof, err = NewCiphertextValidityProof(&randomness42, keys.PublicKey, ciphertext12, message42) require.Nil(t, err) - validated = VerifyCiphertextValidityProof(wrongProof, keys.PublicKey, ciphertext12) + validated = VerifyCiphertextValidity(wrongProof, keys.PublicKey, ciphertext12) require.False(t, validated, "Proof generated with the wrong ciphertext should validate as false") } @@ -143,24 +143,24 @@ func TestVerifyCiphertextValidityProof_Invalid_Input(t *testing.T) { t.Run("Invalid (nil) proof", func(t *testing.T) { // Proof commitment1 is nil - validated := VerifyCiphertextValidityProof(nil, keys.PublicKey, ciphertext) + validated := VerifyCiphertextValidity(nil, keys.PublicKey, ciphertext) require.False(t, validated, "Validation should fail for nil commitment1") }) t.Run("Invalid proof with nil fields", func(t *testing.T) { - validated := VerifyCiphertextValidityProof(&CiphertextValidityProof{}, keys.PublicKey, ciphertext) + validated := VerifyCiphertextValidity(&CiphertextValidityProof{}, keys.PublicKey, ciphertext) require.False(t, validated, "Validation should fail for proof with nil fields") }) t.Run("Invalid Public Key", func(t *testing.T) { // Proof challenge is nil - validated := VerifyCiphertextValidityProof(proof, nil, ciphertext) + validated := VerifyCiphertextValidity(proof, nil, ciphertext) require.False(t, validated, "Validation should fail for nil challenge") }) t.Run("Invalid Ciphertext", func(t *testing.T) { // Proof response1 is nil - validated := VerifyCiphertextValidityProof(proof, keys.PublicKey, nil) + validated := VerifyCiphertextValidity(proof, keys.PublicKey, nil) require.False(t, validated, "Validation should fail for nil response1") }) } From 8d07ac5e36738286053fbe89a225c2e31a0610fd Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 17 Oct 2024 14:59:39 -0700 Subject: [PATCH 25/30] Addressing comments - part 3 --- pkg/zkproofs/ciphertext_validity.go | 3 +- pkg/zkproofs/pubkey_validity.go | 41 ++++++++++++++-------------- pkg/zkproofs/pubkey_validity_test.go | 10 +++---- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/pkg/zkproofs/ciphertext_validity.go b/pkg/zkproofs/ciphertext_validity.go index 1f683fa..9cca7ac 100644 --- a/pkg/zkproofs/ciphertext_validity.go +++ b/pkg/zkproofs/ciphertext_validity.go @@ -78,7 +78,8 @@ func NewCiphertextValidityProof(pedersenOpening *curves.Scalar, pubKey curves.Po }, nil } -// VerifyCiphertextValidity verifies the zero-knowledge proof that a ciphertext is valid. +// VerifyCiphertextValidity verifies that some ciphertext is properly encrypted by the given Public Key, with the help +// of proof sent by the prover. // Parameters: // - proof: The proof to verify. // - pubKey: The public key used in the encryption. diff --git a/pkg/zkproofs/pubkey_validity.go b/pkg/zkproofs/pubkey_validity.go index 0debccb..35f714f 100644 --- a/pkg/zkproofs/pubkey_validity.go +++ b/pkg/zkproofs/pubkey_validity.go @@ -2,7 +2,6 @@ package zkproofs import ( "crypto/rand" - "crypto/sha256" "encoding/json" "errors" "github.com/coinbase/kryptology/pkg/core/curves" @@ -20,8 +19,11 @@ type PubKeyValidityProof struct { Z curves.Scalar } -// NewPubKeyValidityProof generates the sigma protocol proof -// The proof here is that the creator of this PubKey also knows the corresponding PrivateKey +// NewPubKeyValidityProof generates the proof the creator of the given PublicKey also knows the associated PrivateKey, +// and that the PublicKey is thus valid. +// Parameters: +// - pubKey: The PublicKey to prove the validity of. +// - privKey: The PrivateKey associated with the PublicKey. func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) (*PubKeyValidityProof, error) { // Validate input if pubKey == nil { @@ -41,7 +43,10 @@ func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) (*PubKey Y := H.Mul(y) // Generate challenge c based on P and Y - c := generateChallenge(pubKey, Y) + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("P", pubKey.ToAffineCompressed()) + transcript.AppendMessage("Y", Y.ToAffineCompressed()) + c := transcript.ChallengeScalar() // Compute sInv = s^{-1} sInv, _ := privKey.Invert() @@ -55,8 +60,12 @@ func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) (*PubKey }, nil } -// VerifyPubKeyValidityProof verifies the validity of the proof -func VerifyPubKeyValidityProof(pubKey curves.Point, proof PubKeyValidityProof) bool { +// VerifyPubKeyValidity validates that the given PublicKey is a valid PublicKey under the Twisted El Gamal scheme, +// and that the prover knows the corresponding PrivateKey. +// Parameters: +// - pubKey: The PublicKey to validate. +// - proof: The proof that the prover knows the corresponding PrivateKey. +func VerifyPubKeyValidity(pubKey curves.Point, proof PubKeyValidityProof) bool { // Validate input if pubKey == nil || proof.Y == nil || proof.Z == nil { return false @@ -64,8 +73,13 @@ func VerifyPubKeyValidityProof(pubKey curves.Point, proof PubKeyValidityProof) b eg := elgamal.NewTwistedElgamal() H := eg.GetH() + // Recompute the challenge c - c := generateChallenge(pubKey, proof.Y) + transcript := NewEqualityProofTranscript() + transcript.AppendMessage("P", pubKey.ToAffineCompressed()) + transcript.AppendMessage("Y", proof.Y.ToAffineCompressed()) + + c := transcript.ChallengeScalar() // Compute lhs = z * H lhs := H.Mul(proof.Z) // lhs = z * H @@ -78,19 +92,6 @@ func VerifyPubKeyValidityProof(pubKey curves.Point, proof PubKeyValidityProof) b return lhs.Equal(rhs) } -// generateChallenge generates a challenge c by hashing P and Y -func generateChallenge(P, Y curves.Point) curves.Scalar { - // Hash P and Y using SHA-256 - hash := sha256.New() - - hash.Write(P.ToAffineCompressed()) - hash.Write(Y.ToAffineCompressed()) - digest := hash.Sum(nil) - - // Convert hash output into a scalar - return curves.ED25519().Scalar.Hash(digest) -} - // MarshalJSON for PubKeyValidityProof func (p *PubKeyValidityProof) MarshalJSON() ([]byte, error) { // Serialize the points and scalars to a format you prefer diff --git a/pkg/zkproofs/pubkey_validity_test.go b/pkg/zkproofs/pubkey_validity_test.go index 535dd2e..4877c6c 100644 --- a/pkg/zkproofs/pubkey_validity_test.go +++ b/pkg/zkproofs/pubkey_validity_test.go @@ -20,16 +20,16 @@ func TestPubKeyValidityProof(t *testing.T) { require.NoError(t, err) // Verify the proof - valid := VerifyPubKeyValidityProof(keys.PublicKey, *proof) + valid := VerifyPubKeyValidity(keys.PublicKey, *proof) require.True(t, valid, "Valid Proof should be validated as true") - invalid := VerifyPubKeyValidityProof(altKeys.PublicKey, *proof) + invalid := VerifyPubKeyValidity(altKeys.PublicKey, *proof) require.False(t, invalid, "Proof should be invalid when trying to validate wrong PublicKey") // Generate proof with the wrong private key. badProof, err := NewPubKeyValidityProof(keys.PublicKey, altKeys.PrivateKey) require.Nil(t, err) - invalid = VerifyPubKeyValidityProof(keys.PublicKey, *badProof) + invalid = VerifyPubKeyValidity(keys.PublicKey, *badProof) require.False(t, invalid, "Proof generated with wrong Privkey should be validated as false.") } @@ -79,11 +79,11 @@ func TestVerifyPubKeyValidityProof_InvalidInput(t *testing.T) { require.Nil(t, err) // Verify the proof - valid := VerifyPubKeyValidityProof(nil, *proof) + valid := VerifyPubKeyValidity(nil, *proof) require.False(t, valid, "proof verification should fail for nil public key") invalidProof := PubKeyValidityProof{} - valid = VerifyPubKeyValidityProof(keys.PublicKey, invalidProof) + valid = VerifyPubKeyValidity(keys.PublicKey, invalidProof) require.False(t, valid, "proof verification should fail for invalid proof") } From 939d4746ae6f832a8c5256b7c8c7c96aaab0b75d Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 17 Oct 2024 15:01:23 -0700 Subject: [PATCH 26/30] Refactoring transcript to a neutral name --- pkg/zkproofs/ciphertext_ciphertext_equality.go | 4 ++-- pkg/zkproofs/ciphertext_commitment_equality.go | 4 ++-- pkg/zkproofs/ciphertext_validity.go | 4 ++-- pkg/zkproofs/equality_proof_transcript.go | 12 +++++++----- pkg/zkproofs/equality_proof_transcript_test.go | 6 +++--- pkg/zkproofs/pubkey_validity.go | 4 ++-- pkg/zkproofs/zero_balance.go | 4 ++-- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality.go b/pkg/zkproofs/ciphertext_ciphertext_equality.go index 7fe9f6b..ebd4951 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality.go @@ -93,7 +93,7 @@ func NewCiphertextCiphertextEqualityProof( Y3 := pDestination.Mul(yr) // Append to transcript - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("Y0", Y0.ToAffineCompressed()) transcript.AppendMessage("Y1", Y1.ToAffineCompressed()) transcript.AppendMessage("Y2", Y2.ToAffineCompressed()) @@ -156,7 +156,7 @@ func VerifyCiphertextCiphertextEquality( dDestination := destinationCiphertext.D // Recreate the transcript - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() // Append Y0, Y1, Y2, Y3 to transcript transcript.AppendMessage("Y0", proof.Y0.ToAffineCompressed()) transcript.AppendMessage("Y1", proof.Y1.ToAffineCompressed()) diff --git a/pkg/zkproofs/ciphertext_commitment_equality.go b/pkg/zkproofs/ciphertext_commitment_equality.go index 1b81b47..f91deed 100644 --- a/pkg/zkproofs/ciphertext_commitment_equality.go +++ b/pkg/zkproofs/ciphertext_commitment_equality.go @@ -78,7 +78,7 @@ func NewCiphertextCommitmentEqualityProof( Y2 := yxG.Add(yrH) // Append commitments to the transcript - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("Y0", Y0.ToAffineCompressed()) transcript.AppendMessage("Y1", Y1.ToAffineCompressed()) transcript.AppendMessage("Y2", Y2.ToAffineCompressed()) @@ -140,7 +140,7 @@ func VerifyCiphertextCommitmentEquality( H := eg.GetH() // Append commitments to the transcript - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("Y0", proof.Y0.ToAffineCompressed()) transcript.AppendMessage("Y1", proof.Y1.ToAffineCompressed()) transcript.AppendMessage("Y2", proof.Y2.ToAffineCompressed()) diff --git a/pkg/zkproofs/ciphertext_validity.go b/pkg/zkproofs/ciphertext_validity.go index 9cca7ac..ba2e685 100644 --- a/pkg/zkproofs/ciphertext_validity.go +++ b/pkg/zkproofs/ciphertext_validity.go @@ -61,7 +61,7 @@ func NewCiphertextValidityProof(pedersenOpening *curves.Scalar, pubKey curves.Po // Step 3: Generate a challenge using the Fiat-Shamir heuristic. // The challenge is basically just a hash of all the provided values. This locks in the values and makes sure that // the proof cannot be for some other set of values. - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("C1", Commitment1.ToAffineCompressed()) transcript.AppendMessage("C2", Commitment2.ToAffineCompressed()) challenge := transcript.ChallengeScalar() @@ -96,7 +96,7 @@ func VerifyCiphertextValidity(proof *CiphertextValidityProof, pubKey curves.Poin G := eg.GetG() // Step 0: Recompute the challenge using the Fiat-Shamir heuristic. - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("C1", proof.Commitment1.ToAffineCompressed()) transcript.AppendMessage("C2", proof.Commitment2.ToAffineCompressed()) challenge := transcript.ChallengeScalar() diff --git a/pkg/zkproofs/equality_proof_transcript.go b/pkg/zkproofs/equality_proof_transcript.go index 22cb955..a99b3c1 100644 --- a/pkg/zkproofs/equality_proof_transcript.go +++ b/pkg/zkproofs/equality_proof_transcript.go @@ -6,21 +6,23 @@ import ( "github.com/coinbase/kryptology/pkg/core/curves" ) -type EqualityProofTranscript struct { +// ProofTranscript represents a transcript of messages used in a zero-knowledge proof +type ProofTranscript struct { messages [][]byte } -func NewEqualityProofTranscript() *EqualityProofTranscript { - return &EqualityProofTranscript{messages: make([][]byte, 0)} +// NewProofTranscript creates a new proof transcript +func NewProofTranscript() *ProofTranscript { + return &ProofTranscript{messages: make([][]byte, 0)} } // AppendMessage appends a message to the transcript -func (t *EqualityProofTranscript) AppendMessage(label string, data []byte) { +func (t *ProofTranscript) AppendMessage(label string, data []byte) { t.messages = append(t.messages, append([]byte(label), data...)) } // ChallengeScalar generates a challenge scalar from the transcript -func (t *EqualityProofTranscript) ChallengeScalar() curves.Scalar { +func (t *ProofTranscript) ChallengeScalar() curves.Scalar { hasher := sha512.New() for _, msg := range t.messages { hasher.Write(msg) diff --git a/pkg/zkproofs/equality_proof_transcript_test.go b/pkg/zkproofs/equality_proof_transcript_test.go index 6e86870..4637e46 100644 --- a/pkg/zkproofs/equality_proof_transcript_test.go +++ b/pkg/zkproofs/equality_proof_transcript_test.go @@ -8,7 +8,7 @@ import ( ) func TestEqualityProofTranscript_AppendMessage(t *testing.T) { - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("label1", []byte("data1")) transcript.AppendMessage("label2", []byte("data2")) @@ -18,7 +18,7 @@ func TestEqualityProofTranscript_AppendMessage(t *testing.T) { } func TestEqualityProofTranscript_ChallengeScalar(t *testing.T) { - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("label1", []byte("data1")) transcript.AppendMessage("label2", []byte("data2")) @@ -34,7 +34,7 @@ func TestEqualityProofTranscript_ChallengeScalar(t *testing.T) { } func TestEqualityProofTranscript_EmptyMessages(t *testing.T) { - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() scalar := transcript.ChallengeScalar() hasher := sha512.New() diff --git a/pkg/zkproofs/pubkey_validity.go b/pkg/zkproofs/pubkey_validity.go index 35f714f..145bb83 100644 --- a/pkg/zkproofs/pubkey_validity.go +++ b/pkg/zkproofs/pubkey_validity.go @@ -43,7 +43,7 @@ func NewPubKeyValidityProof(pubKey curves.Point, privKey curves.Scalar) (*PubKey Y := H.Mul(y) // Generate challenge c based on P and Y - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("P", pubKey.ToAffineCompressed()) transcript.AppendMessage("Y", Y.ToAffineCompressed()) c := transcript.ChallengeScalar() @@ -75,7 +75,7 @@ func VerifyPubKeyValidity(pubKey curves.Point, proof PubKeyValidityProof) bool { H := eg.GetH() // Recompute the challenge c - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("P", pubKey.ToAffineCompressed()) transcript.AppendMessage("Y", proof.Y.ToAffineCompressed()) diff --git a/pkg/zkproofs/zero_balance.go b/pkg/zkproofs/zero_balance.go index 711e757..cdd1259 100644 --- a/pkg/zkproofs/zero_balance.go +++ b/pkg/zkproofs/zero_balance.go @@ -41,7 +41,7 @@ func NewZeroBalanceProof( Yd := D.Mul(y) // Append commitments to the transcript - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("Yp", Yp.ToAffineCompressed()) transcript.AppendMessage("Yd", Yd.ToAffineCompressed()) @@ -78,7 +78,7 @@ func VerifyZeroProof( H := eg.GetH() // Append commitments to the transcript - transcript := NewEqualityProofTranscript() + transcript := NewProofTranscript() transcript.AppendMessage("Yp", proof.Yp.ToAffineCompressed()) transcript.AppendMessage("Yd", proof.Yd.ToAffineCompressed()) From bd8bc81b2f9c2f755131012d654dac4c5867baa0 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 17 Oct 2024 15:19:28 -0700 Subject: [PATCH 27/30] Addressing comments - part 4 --- pkg/zkproofs/range.go | 16 +++++++++++----- pkg/zkproofs/range_test.go | 14 +++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pkg/zkproofs/range.go b/pkg/zkproofs/range.go index 06fedee..772819d 100644 --- a/pkg/zkproofs/range.go +++ b/pkg/zkproofs/range.go @@ -19,9 +19,11 @@ type RangeProof struct { UpperBound int } -// NewRangeProof a range proof for some ciphertext that proves the value is between 0 and 2^upperBound -// Note that while this proof generator takes in the plaintext of the value, it will only work in conjunction with a -// ciphertext that encrypts value using the given randomness. +// NewRangeProof generates a range proof for some ciphertext that proves the value is between 0 and 2^upperBound +// Parameters: +// - upperBound The upper bound of the range we want to prove the value lies within, calculated as 2^upperBound +// - value: The value encrypted by the ciphertext on which we are creating this proof for +// - randomness: The randomness used in the generation of the ciphertext on which we are creating this proof for func NewRangeProof(upperBound, value int, randomness curves.Scalar) (*RangeProof, error) { if randomness == nil { return nil, errors.New("invalid randomness factor") @@ -52,7 +54,11 @@ func NewRangeProof(upperBound, value int, randomness curves.Scalar) (*RangeProof } // VerifyRangeProof verifies the range proof for the given ciphertext -func VerifyRangeProof(proof *RangeProof, ciphertext *elgamal.Ciphertext) (bool, error) { +// Parameters: +// - proof: The range proof to verify +// - ciphertext: The ciphertext for which we are verifying the range proof +// - upperBound: The upper bound of the range we want to prove the value lies within, calculated as 2^upperBound +func VerifyRangeProof(proof *RangeProof, ciphertext *elgamal.Ciphertext, upperBound int) (bool, error) { // Validate input if proof == nil || proof.Proof == nil || proof.Randomness == nil { return false, errors.New("invalid proof") @@ -64,7 +70,7 @@ func VerifyRangeProof(proof *RangeProof, ciphertext *elgamal.Ciphertext) (bool, curve := curves.ED25519() // Verifier gets the proof, the commitment, the generators to verify the value is within the range - verifier, err := bulletproof.NewRangeVerifier(proof.UpperBound, getRangeDomain(), getIppDomain(), *curve) + verifier, err := bulletproof.NewRangeVerifier(upperBound, getRangeDomain(), getIppDomain(), *curve) if err != nil { return false, err } diff --git a/pkg/zkproofs/range_test.go b/pkg/zkproofs/range_test.go index 9640ce0..5846cb8 100644 --- a/pkg/zkproofs/range_test.go +++ b/pkg/zkproofs/range_test.go @@ -160,7 +160,7 @@ func TestRangeProofs(t *testing.T) { proof, err := NewRangeProof(n, value, gamma) require.Nil(t, err) - verified, err := VerifyRangeProof(proof, ciphertext) + verified, err := VerifyRangeProof(proof, ciphertext, n) require.NoError(t, err) require.True(t, verified) @@ -168,7 +168,7 @@ func TestRangeProofs(t *testing.T) { ciphertext101, _, err := eg.Encrypt(keyPair.PublicKey, uint64(101)) require.Nil(t, err) - verified, err = VerifyRangeProof(proof, ciphertext101) + verified, err = VerifyRangeProof(proof, ciphertext101, n) require.Error(t, err) require.False(t, verified) } @@ -200,7 +200,7 @@ func TestRangeProofsWithMarshaling(t *testing.T) { err = json.Unmarshal(marshaledProof, &unmarshaled) require.NoError(t, err, "Unmarshaling should not produce an error") - verified, err := VerifyRangeProof(&unmarshaled, ciphertext) + verified, err := VerifyRangeProof(&unmarshaled, ciphertext, n) require.NoError(t, err) require.True(t, verified) } @@ -243,27 +243,27 @@ func TestVerifyRangeProof_InvalidInput(t *testing.T) { t.Run("Nil proof", func(t *testing.T) { // Proof is nil - valid, err := VerifyRangeProof(nil, ciphertext) + valid, err := VerifyRangeProof(nil, ciphertext, 64) require.EqualError(t, err, "invalid proof") require.False(t, valid) }) t.Run("Proof with nil fields", func(t *testing.T) { - valid, err := VerifyRangeProof(&RangeProof{}, ciphertext) + valid, err := VerifyRangeProof(&RangeProof{}, ciphertext, 64) require.EqualError(t, err, "invalid proof") require.False(t, valid) }) t.Run("nil ciphertext", func(t *testing.T) { // Proof is nil - valid, err := VerifyRangeProof(proof, nil) + valid, err := VerifyRangeProof(proof, nil, 64) require.EqualError(t, err, "invalid ciphertext") require.False(t, valid) }) t.Run("Ciphertext with nil fields", func(t *testing.T) { // Proof is nil - valid, err := VerifyRangeProof(proof, &elgamal.Ciphertext{}) + valid, err := VerifyRangeProof(proof, &elgamal.Ciphertext{}, 64) require.EqualError(t, err, "invalid ciphertext") require.False(t, valid) }) From 693c1c3b144f3b5800924ace7a51d5938f11b952 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Thu, 17 Oct 2024 15:22:30 -0700 Subject: [PATCH 28/30] Addressing comments - part 5 --- pkg/zkproofs/zero_balance.go | 11 +++++++++-- pkg/zkproofs/zero_balance_test.go | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/pkg/zkproofs/zero_balance.go b/pkg/zkproofs/zero_balance.go index cdd1259..563708f 100644 --- a/pkg/zkproofs/zero_balance.go +++ b/pkg/zkproofs/zero_balance.go @@ -16,6 +16,9 @@ type ZeroBalanceProof struct { } // NewZeroBalanceProof generates a zero-knowledge proof that a ciphertext encrypts 0 number. +// Parameters: +// - keypair: The Twisted El Gamal Keypair used to generate the given ciphertext +// - ciphertext: The Ciphertext which we want to prove encrypts a value of 0 func NewZeroBalanceProof( keypair *elgamal.KeyPair, ciphertext *elgamal.Ciphertext, @@ -58,8 +61,12 @@ func NewZeroBalanceProof( }, nil } -// VerifyZeroProof verifies the validity of the proof -func VerifyZeroProof( +// VerifyZeroBalance verifies the that ciphertext encrypts 0 number. +// Parameters: +// - proof: The ZeroBalanceProof to verify +// - pubKey: The public key used to encrypt the ciphertext +// - ciphertext: The ciphertext to verify +func VerifyZeroBalance( proof *ZeroBalanceProof, pubKey *curves.Point, ciphertext *elgamal.Ciphertext, diff --git a/pkg/zkproofs/zero_balance_test.go b/pkg/zkproofs/zero_balance_test.go index 070ee2d..92dcc9c 100644 --- a/pkg/zkproofs/zero_balance_test.go +++ b/pkg/zkproofs/zero_balance_test.go @@ -63,7 +63,7 @@ func TestZeroBalanceProof(t *testing.T) { } // Verify the proof - valid := VerifyZeroProof(proof, &keypair.PublicKey, ciphertext) + valid := VerifyZeroBalance(proof, &keypair.PublicKey, ciphertext) if valid != tt.expectValid { t.Errorf("Expected proof validity to be %v, got %v", tt.expectValid, valid) } @@ -119,7 +119,7 @@ func TestZeroBalanceProof_InvalidRandomness(t *testing.T) { } // Verify the proof - valid := VerifyZeroProof(invalidProof, &keypair.PublicKey, ciphertext) + valid := VerifyZeroBalance(invalidProof, &keypair.PublicKey, ciphertext) require.False(t, valid, "Proof with invalid randomness fields should fail verification") } @@ -149,7 +149,7 @@ func TestZeroBalanceProof_ExtremelyLargeScalars(t *testing.T) { proof.Z = largeScalar // Verify the proof - valid := VerifyZeroProof(proof, &keypair.PublicKey, ciphertext) + valid := VerifyZeroBalance(proof, &keypair.PublicKey, ciphertext) require.False(t, valid, "Proof with extremely large scalar Z should fail verification") } @@ -242,29 +242,29 @@ func TestVerifyZeroProof_InvalidInput(t *testing.T) { require.NoError(t, err, "Failed to generate proof") // Test with nil proof - valid := VerifyZeroProof(nil, &keypair.PublicKey, ciphertext) + valid := VerifyZeroBalance(nil, &keypair.PublicKey, ciphertext) require.False(t, valid, "Verification should fail when proof is nil") nilFieldsProof := &ZeroBalanceProof{} - valid = VerifyZeroProof(nilFieldsProof, &keypair.PublicKey, ciphertext) + valid = VerifyZeroBalance(nilFieldsProof, &keypair.PublicKey, ciphertext) require.False(t, valid, "Verification should fail when proof has nil fields") // Test with nil public key - valid = VerifyZeroProof(proof, nil, ciphertext) + valid = VerifyZeroBalance(proof, nil, ciphertext) require.False(t, valid, "Verification should fail when public key is nil") // Test with nil ciphertext - valid = VerifyZeroProof(proof, &keypair.PublicKey, nil) + valid = VerifyZeroBalance(proof, &keypair.PublicKey, nil) require.False(t, valid, "Verification should fail when ciphertext is nil") // Test with nil D in ciphertext invalidCiphertext1 := &elgamal.Ciphertext{C: keypair.PublicKey, D: nil} - valid = VerifyZeroProof(proof, &keypair.PublicKey, invalidCiphertext1) + valid = VerifyZeroBalance(proof, &keypair.PublicKey, invalidCiphertext1) require.False(t, valid, "Verification should fail when ciphertext.D is nil") // Test with nil C in ciphertext invalidCiphertext2 := &elgamal.Ciphertext{C: nil, D: keypair.PublicKey} - valid = VerifyZeroProof(proof, &keypair.PublicKey, invalidCiphertext2) + valid = VerifyZeroBalance(proof, &keypair.PublicKey, invalidCiphertext2) require.False(t, valid, "Verification should fail when ciphertext.C is nil") // Test with invalid proof values @@ -274,6 +274,6 @@ func TestVerifyZeroProof_InvalidInput(t *testing.T) { Yd: curve.Point.Double(), Z: curve.Scalar.Zero(), } - valid = VerifyZeroProof(invalidProof, &keypair.PublicKey, ciphertext) + valid = VerifyZeroBalance(invalidProof, &keypair.PublicKey, ciphertext) require.False(t, valid, "Verification should fail with invalid proof values") } From 5a3b716411e17611a5846a54a50bc91408fbcc62 Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Tue, 22 Oct 2024 08:03:20 +0300 Subject: [PATCH 29/30] Address review comments --- pkg/zkproofs/ciphertext_validity.go | 4 ++++ .../{equality_proof_transcript.go => proof_transcript.go} | 0 ...lity_proof_transcript_test.go => proof_transcript_test.go} | 0 3 files changed, 4 insertions(+) rename pkg/zkproofs/{equality_proof_transcript.go => proof_transcript.go} (100%) rename pkg/zkproofs/{equality_proof_transcript_test.go => proof_transcript_test.go} (100%) diff --git a/pkg/zkproofs/ciphertext_validity.go b/pkg/zkproofs/ciphertext_validity.go index ba2e685..1efc60b 100644 --- a/pkg/zkproofs/ciphertext_validity.go +++ b/pkg/zkproofs/ciphertext_validity.go @@ -64,6 +64,8 @@ func NewCiphertextValidityProof(pedersenOpening *curves.Scalar, pubKey curves.Po transcript := NewProofTranscript() transcript.AppendMessage("C1", Commitment1.ToAffineCompressed()) transcript.AppendMessage("C2", Commitment2.ToAffineCompressed()) + transcript.AppendMessage("C3", ciphertext.C.ToAffineCompressed()) + transcript.AppendMessage("C4", ciphertext.D.ToAffineCompressed()) challenge := transcript.ChallengeScalar() // Step 4: Generate responses @@ -99,6 +101,8 @@ func VerifyCiphertextValidity(proof *CiphertextValidityProof, pubKey curves.Poin transcript := NewProofTranscript() transcript.AppendMessage("C1", proof.Commitment1.ToAffineCompressed()) transcript.AppendMessage("C2", proof.Commitment2.ToAffineCompressed()) + transcript.AppendMessage("C3", ciphertext.C.ToAffineCompressed()) + transcript.AppendMessage("C4", ciphertext.D.ToAffineCompressed()) challenge := transcript.ChallengeScalar() // Step 1: Recompute Commitment1 diff --git a/pkg/zkproofs/equality_proof_transcript.go b/pkg/zkproofs/proof_transcript.go similarity index 100% rename from pkg/zkproofs/equality_proof_transcript.go rename to pkg/zkproofs/proof_transcript.go diff --git a/pkg/zkproofs/equality_proof_transcript_test.go b/pkg/zkproofs/proof_transcript_test.go similarity index 100% rename from pkg/zkproofs/equality_proof_transcript_test.go rename to pkg/zkproofs/proof_transcript_test.go From a953dd943f5cdaab4e3513fc405a9fa59480428a Mon Sep 17 00:00:00 2001 From: _dssei_ Date: Wed, 23 Oct 2024 17:40:32 +0300 Subject: [PATCH 30/30] Move GenerateKey to test utils --- pkg/encryption/elgamal/common.go | 7 ----- pkg/encryption/elgamal/encryption_test.go | 29 ++++++++++--------- pkg/encryption/elgamal/types_test.go | 3 +- pkg/testing/utils.go | 12 ++++++++ .../ciphertext_ciphertext_equality_test.go | 27 ++++++++--------- .../ciphertext_commitment_equality_test.go | 9 +++--- pkg/zkproofs/ciphertext_validity_test.go | 11 +++---- pkg/zkproofs/pubkey_validity_test.go | 11 +++---- pkg/zkproofs/range_test.go | 13 +++++---- pkg/zkproofs/zero_balance_test.go | 17 ++++++----- 10 files changed, 76 insertions(+), 63 deletions(-) create mode 100644 pkg/testing/utils.go diff --git a/pkg/encryption/elgamal/common.go b/pkg/encryption/elgamal/common.go index 0f6ca5b..fe5e42a 100644 --- a/pkg/encryption/elgamal/common.go +++ b/pkg/encryption/elgamal/common.go @@ -2,9 +2,7 @@ package elgamal import ( "crypto/ecdsa" - "crypto/rand" "crypto/sha256" - "github.com/ethereum/go-ethereum/crypto/secp256k1" "io" "github.com/coinbase/kryptology/pkg/core/curves" @@ -14,11 +12,6 @@ import ( // H_STRING H is a random point on the elliptic curve that is unrelated to G. const H_STRING = "gPt25pi0eDphSiXWu0BIeIvyVATCtwhslTqfqvNhW2c" -// GenerateKey generates a new ECDSA key pair. -func GenerateKey() (*ecdsa.PrivateKey, error) { - return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader) -} - // KeyGen generates a new key pair for the Twisted ElGamal encryption scheme. func (teg TwistedElGamal) KeyGen(privateKey ecdsa.PrivateKey, denom string) (*KeyPair, error) { // Fixed base point H diff --git a/pkg/encryption/elgamal/encryption_test.go b/pkg/encryption/elgamal/encryption_test.go index 7660f59..83686d1 100644 --- a/pkg/encryption/elgamal/encryption_test.go +++ b/pkg/encryption/elgamal/encryption_test.go @@ -3,6 +3,7 @@ package elgamal import ( "crypto/rand" "github.com/coinbase/kryptology/pkg/core/curves" + testutils "github.com/sei-protocol/sei-cryptography/pkg/testing" "github.com/stretchr/testify/require" "math" "testing" @@ -11,7 +12,7 @@ import ( const DefaultTestDenom = "factory/sei1239081236472sd/testToken" func TestKeyGeneration(t *testing.T) { - privateKey, err := GenerateKey() + privateKey, err := testutils.GenerateKey() require.Nil(t, err) eg := NewTwistedElgamal() @@ -36,7 +37,7 @@ func TestKeyGeneration(t *testing.T) { require.NotEqual(t, keyPair, keyPairDiffSalt, "PK should be different for different salt") // Test that different privateKey should generate different PK - altPrivateKey, err := GenerateKey() + altPrivateKey, err := testutils.GenerateKey() require.Nil(t, err) keyPairDiffPK, err := eg.KeyGen(*altPrivateKey, altDenom) require.Nil(t, err) @@ -44,8 +45,8 @@ func TestKeyGeneration(t *testing.T) { } func TestEncryptionDecryption(t *testing.T) { - privateKey, _ := GenerateKey() - altPrivateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() + altPrivateKey, _ := testutils.GenerateKey() eg := NewTwistedElgamal() @@ -80,7 +81,7 @@ func TestEncryptionDecryption(t *testing.T) { // Due to the size of 48 bit numbers, this test takes a really long time (~1hr) to run. func Test48BitEncryptionDecryption(t *testing.T) { - privateKey, err := GenerateKey() + privateKey, err := testutils.GenerateKey() require.Nil(t, err) eg := NewTwistedElgamal() @@ -121,8 +122,8 @@ func Test48BitEncryptionDecryption(t *testing.T) { } func TestAddCiphertext(t *testing.T) { - privateKey, _ := GenerateKey() - altPrivateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() + altPrivateKey, _ := testutils.GenerateKey() eg := NewTwistedElgamal() @@ -169,7 +170,7 @@ func TestAddCiphertext(t *testing.T) { func TestTwistedElGamal_InvalidCiphertext(t *testing.T) { eg := NewTwistedElgamal() - privateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) invalidCt := &Ciphertext{} @@ -184,7 +185,7 @@ func TestTwistedElGamal_NilPrivateKey(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) // Encrypt a value with a valid public key @@ -203,7 +204,7 @@ func TestTwistedElGamal_EncryptDecryptWithRand(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) message := uint64(555555555) @@ -221,7 +222,7 @@ func TestTwistedElGamal_EncryptMessageTwice(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) message := uint64(555555555) @@ -237,7 +238,7 @@ func TestTwistedElGamal_DecryptWithZeroBits(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) message := uint64(555555555) @@ -263,7 +264,7 @@ func TestTwistedElGamal_EncryptInvalidRandomFactor(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) // Test with nil public key @@ -276,7 +277,7 @@ func TestTwistedElGamal_EncryptBoundaryValues(t *testing.T) { eg := NewTwistedElgamal() // Generate a valid key pair for comparison - privateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) // Test with the smallest possible value (0) diff --git a/pkg/encryption/elgamal/types_test.go b/pkg/encryption/elgamal/types_test.go index 5ad76f0..1403075 100644 --- a/pkg/encryption/elgamal/types_test.go +++ b/pkg/encryption/elgamal/types_test.go @@ -2,12 +2,13 @@ package elgamal import ( "encoding/json" + testutils "github.com/sei-protocol/sei-cryptography/pkg/testing" "github.com/stretchr/testify/require" "testing" ) func TestCiphertext_MarshalJSON(t *testing.T) { - privateKey, _ := GenerateKey() + privateKey, _ := testutils.GenerateKey() eg := NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, DefaultTestDenom) diff --git a/pkg/testing/utils.go b/pkg/testing/utils.go new file mode 100644 index 0000000..6e9f8d0 --- /dev/null +++ b/pkg/testing/utils.go @@ -0,0 +1,12 @@ +package testing + +import ( + "crypto/ecdsa" + "crypto/rand" + "github.com/ethereum/go-ethereum/crypto/secp256k1" +) + +// GenerateKey generates a new ECDSA key pair. +func GenerateKey() (*ecdsa.PrivateKey, error) { + return ecdsa.GenerateKey(secp256k1.S256(), rand.Reader) +} diff --git a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go index 62a9f64..9af50a3 100644 --- a/pkg/zkproofs/ciphertext_ciphertext_equality_test.go +++ b/pkg/zkproofs/ciphertext_ciphertext_equality_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + testutils "github.com/sei-protocol/sei-cryptography/pkg/testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "math/big" @@ -53,15 +54,15 @@ func TestCiphertextCiphertextEqualityProof(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Key generation - sourcePrivateKey, _ := elgamal.GenerateKey() - destPrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() + destPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) var actualDestinationPubkey *curves.Point if tt.useDifferentPublicKey { - altDestPrivateKey, _ := elgamal.GenerateKey() + altDestPrivateKey, _ := testutils.GenerateKey() // Generate an alternative keypair for destination altDestinationKeypair, _ := eg.KeyGen(*altDestPrivateKey, TestDenom) actualDestinationPubkey = &altDestinationKeypair.PublicKey @@ -111,8 +112,8 @@ func TestCiphertextCiphertextEqualityProof(t *testing.T) { func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { t.Run("Zero Amounts", func(t *testing.T) { // Key generation - sourcePrivateKey, _ := elgamal.GenerateKey() - destPrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() + destPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) @@ -152,8 +153,8 @@ func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { t.Run("Maximum Amount", func(t *testing.T) { // Key generation - sourcePrivateKey, _ := elgamal.GenerateKey() - destPrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() + destPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) @@ -195,8 +196,8 @@ func TestCiphertextCiphertextEqualityProof_EdgeCases(t *testing.T) { } func TestCiphertextCiphertextEqualityProof_UnmarshalJSON_Valid(t *testing.T) { - sourcePrivateKey, _ := elgamal.GenerateKey() - destPrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() + destPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) @@ -243,8 +244,8 @@ func TestCiphertextCiphertextEqualityProof_UnmarshalJSON_Valid(t *testing.T) { // Invalid input tests for NewCiphertextCiphertextEqualityProof func TestNewCiphertextCiphertextEqualityProof_InvalidInputs(t *testing.T) { - sourcePrivateKey, _ := elgamal.GenerateKey() - destPrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() + destPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) @@ -326,8 +327,8 @@ func TestNewCiphertextCiphertextEqualityProof_InvalidInputs(t *testing.T) { // Invalid input tests for VerifyCiphertextCiphertextEquality func TestVerifyCiphertextCiphertextEquality_InvalidInputs(t *testing.T) { - sourcePrivateKey, _ := elgamal.GenerateKey() - destPrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() + destPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) destinationKeypair, _ := eg.KeyGen(*destPrivateKey, TestDenom) diff --git a/pkg/zkproofs/ciphertext_commitment_equality_test.go b/pkg/zkproofs/ciphertext_commitment_equality_test.go index 28e5c7a..c86ce05 100644 --- a/pkg/zkproofs/ciphertext_commitment_equality_test.go +++ b/pkg/zkproofs/ciphertext_commitment_equality_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + testutils "github.com/sei-protocol/sei-cryptography/pkg/testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "math/big" @@ -52,7 +53,7 @@ func TestCiphertextCommitmentEqualityProof(t *testing.T) { tt := tt // Capture range variable t.Run(tt.name, func(t *testing.T) { // Key generation - sourcePrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) @@ -120,7 +121,7 @@ func TestCiphertextCommitmentEqualityProof(t *testing.T) { } func TestCiphertextCommitmentEqualityProof_MarshalUnmarshalJSON(t *testing.T) { - sourcePrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) @@ -161,7 +162,7 @@ func TestCiphertextCommitmentEqualityProof_MarshalUnmarshalJSON(t *testing.T) { } func TestNewCiphertextCommitmentEqualityProof_InvalidInput(t *testing.T) { - sourcePrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) @@ -222,7 +223,7 @@ func TestNewCiphertextCommitmentEqualityProof_InvalidInput(t *testing.T) { } func TestVerifyCiphertextCommitmentEquality_InvalidInput(t *testing.T) { - sourcePrivateKey, _ := elgamal.GenerateKey() + sourcePrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() sourceKeypair, _ := eg.KeyGen(*sourcePrivateKey, TestDenom) diff --git a/pkg/zkproofs/ciphertext_validity_test.go b/pkg/zkproofs/ciphertext_validity_test.go index 02d4423..ecc557b 100644 --- a/pkg/zkproofs/ciphertext_validity_test.go +++ b/pkg/zkproofs/ciphertext_validity_test.go @@ -3,13 +3,14 @@ package zkproofs import ( "encoding/json" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + testutils "github.com/sei-protocol/sei-cryptography/pkg/testing" "github.com/stretchr/testify/require" "testing" ) func TestValidityProof(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() - altPrivateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() + altPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, TestDenom) @@ -59,7 +60,7 @@ func TestValidityProof(t *testing.T) { } func TestCiphertextValidityProof_MarshalUnmarshalJSON(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, TestDenom) @@ -85,7 +86,7 @@ func TestCiphertextValidityProof_MarshalUnmarshalJSON(t *testing.T) { } func TestNewCiphertextValidityProof_InvalidInput(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, TestDenom) @@ -131,7 +132,7 @@ func TestNewCiphertextValidityProof_InvalidInput(t *testing.T) { } func TestVerifyCiphertextValidityProof_Invalid_Input(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, TestDenom) diff --git a/pkg/zkproofs/pubkey_validity_test.go b/pkg/zkproofs/pubkey_validity_test.go index 4877c6c..2594531 100644 --- a/pkg/zkproofs/pubkey_validity_test.go +++ b/pkg/zkproofs/pubkey_validity_test.go @@ -3,13 +3,14 @@ package zkproofs import ( "encoding/json" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + testutils "github.com/sei-protocol/sei-cryptography/pkg/testing" "github.com/stretchr/testify/require" "testing" ) func TestPubKeyValidityProof(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() - altPrivateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() + altPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, TestDenom) @@ -34,7 +35,7 @@ func TestPubKeyValidityProof(t *testing.T) { } func TestPubKeyValidityProof_MarshalUnmarshalJSON(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, TestDenom) @@ -55,7 +56,7 @@ func TestPubKeyValidityProof_MarshalUnmarshalJSON(t *testing.T) { } func TestNewPubKeyValidityProof_InvalidInput(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keys, _ := eg.KeyGen(*privateKey, TestDenom) @@ -67,7 +68,7 @@ func TestNewPubKeyValidityProof_InvalidInput(t *testing.T) { } func TestVerifyPubKeyValidityProof_InvalidInput(t *testing.T) { - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.Nil(t, err) eg := elgamal.NewTwistedElgamal() diff --git a/pkg/zkproofs/range_test.go b/pkg/zkproofs/range_test.go index 5846cb8..1f74c81 100644 --- a/pkg/zkproofs/range_test.go +++ b/pkg/zkproofs/range_test.go @@ -7,6 +7,7 @@ import ( "github.com/coinbase/kryptology/pkg/core/curves" "github.com/gtank/merlin" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + testutils "github.com/sei-protocol/sei-cryptography/pkg/testing" "github.com/stretchr/testify/require" "testing" ) @@ -18,7 +19,7 @@ func TestValueIsInRange(t *testing.T) { v := curve.Scalar.New(value) n := 64 // the range is [0, 2^64] - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.Nil(t, err, "Error generating private key") eg := elgamal.NewTwistedElgamal() @@ -59,7 +60,7 @@ func TestRangeAttacksAreInfeasible(t *testing.T) { v := curve.Scalar.New(value) n := 64 // the range is [0, 2^64] - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.Nil(t, err, "Error generating private key") eg := elgamal.NewTwistedElgamal() @@ -148,7 +149,7 @@ func TestRangeProofs(t *testing.T) { value := 100 n := 64 // the range is [0, 2^64] - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.Nil(t, err, "Error generating private key") eg := elgamal.NewTwistedElgamal() @@ -179,7 +180,7 @@ func TestRangeProofsWithMarshaling(t *testing.T) { value := 100 n := 64 // the range is [0, 2^64] - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.Nil(t, err, "Error generating private key") eg := elgamal.NewTwistedElgamal() @@ -206,7 +207,7 @@ func TestRangeProofsWithMarshaling(t *testing.T) { } func TestRangeProofs_InvalidInput(t *testing.T) { - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.Nil(t, err, "Error generating private key") eg := elgamal.NewTwistedElgamal() @@ -233,7 +234,7 @@ func TestRangeProofs_InvalidInput(t *testing.T) { } func TestVerifyRangeProof_InvalidInput(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keyPair, _ := eg.KeyGen(*privateKey, TestDenom) ciphertext, gamma, _ := eg.Encrypt(keyPair.PublicKey, uint64(10)) diff --git a/pkg/zkproofs/zero_balance_test.go b/pkg/zkproofs/zero_balance_test.go index 92dcc9c..8730f66 100644 --- a/pkg/zkproofs/zero_balance_test.go +++ b/pkg/zkproofs/zero_balance_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "github.com/coinbase/kryptology/pkg/core/curves" "github.com/sei-protocol/sei-cryptography/pkg/encryption/elgamal" + testutils "github.com/sei-protocol/sei-cryptography/pkg/testing" "github.com/stretchr/testify/require" "testing" ) @@ -39,8 +40,8 @@ func TestZeroBalanceProof(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Setup keypair - privateKey, _ := elgamal.GenerateKey() - altPrivateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() + altPrivateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keypair, _ := eg.KeyGen(*privateKey, TestDenom) @@ -72,7 +73,7 @@ func TestZeroBalanceProof(t *testing.T) { } func TestZeroBalanceProof_MarshalUnmarshalJSON(t *testing.T) { - privateKey, _ := elgamal.GenerateKey() + privateKey, _ := testutils.GenerateKey() eg := elgamal.NewTwistedElgamal() keypair, _ := eg.KeyGen(*privateKey, TestDenom) @@ -97,7 +98,7 @@ func TestZeroBalanceProof_MarshalUnmarshalJSON(t *testing.T) { } func TestZeroBalanceProof_InvalidRandomness(t *testing.T) { - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.NoError(t, err, "Failed to generate private key") eg := elgamal.NewTwistedElgamal() @@ -124,7 +125,7 @@ func TestZeroBalanceProof_InvalidRandomness(t *testing.T) { } func TestZeroBalanceProof_ExtremelyLargeScalars(t *testing.T) { - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.NoError(t, err, "Failed to generate private key") eg := elgamal.NewTwistedElgamal() @@ -154,7 +155,7 @@ func TestZeroBalanceProof_ExtremelyLargeScalars(t *testing.T) { } func TestZeroBalanceProof_TamperedProof(t *testing.T) { - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.NoError(t, err, "Failed to generate private key") eg := elgamal.NewTwistedElgamal() @@ -200,7 +201,7 @@ func TestZeroBalanceProof_InvalidInput(t *testing.T) { require.Error(t, err, "Should return an error when ciphertext is nil") require.Contains(t, err.Error(), "keypair is invalid") - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.NoError(t, err, "Failed to generate private key") eg := elgamal.NewTwistedElgamal() @@ -228,7 +229,7 @@ func TestZeroBalanceProof_InvalidInput(t *testing.T) { } func TestVerifyZeroProof_InvalidInput(t *testing.T) { - privateKey, err := elgamal.GenerateKey() + privateKey, err := testutils.GenerateKey() require.NoError(t, err, "Failed to generate private key") eg := elgamal.NewTwistedElgamal()