Skip to content

Commit

Permalink
improved test
Browse files Browse the repository at this point in the history
  • Loading branch information
twingdev committed Apr 9, 2024
1 parent 0a16fec commit 38f78bd
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 93 deletions.
Binary file added .DS_Store
Binary file not shown.
90 changes: 40 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,63 @@
Sure, here's a draft of a README.md file that describes the ZK13 algorithm and its implementation in go-libzk13:

# ZK13 Protocol Implementation in Go
ZK13: A Zero-Knowledge Proof Protocol for Set Membership
=======================================================

This repository contains an implementation of the ZK13 protocol, a cryptographic scheme for zero-knowledge proofs, written in Go. The ZK13 protocol allows a prover (Bob) to demonstrate knowledge of a secret to a verifier (Alice) without revealing the secret itself. This implementation emphasizes variable prime lengths for security and performance testing.
ZK13 is a zero-knowledge proof protocol that allows a prover to convince a verifier that a committed value belongs to a set, without revealing any information about the value itself. The protocol is based on the [Pedersen commitment scheme](https://en.wikipedia.org/wiki/Pedersen_commitment) and the [Schwartz-Zippel lemma](https://en.wikipedia.org/wiki/Schwartz%E2%80%93Zippel_lemma).

## Features
The ZK13 protocol has applications in privacy-preserving cryptographic protocols, such as anonymous credentials, electronic voting, and private set intersection.

- Implementation of the ZK13 protocol in Go.
- Support for variable prime lengths to adjust security levels.
- Performance analysis using Go's `pprof` for CPU and memory profiling.
- Demonstrates proof generation and verification processes.
Go-libzk13 is an implementation of the ZK13 protocol in Go. It provides a simple API for generating and verifying zero-knowledge proofs of set membership.

## Getting Started
Algorithm Description
---------------------

### Prerequisites
The ZK13 protocol consists of three main phases:

Ensure you have Go installed on your system. This project was developed with Go version 1.15 or newer. You can check your Go version using:
1. **Commitment Phase:** The prover commits to a value `x` by computing a Pedersen commitment `C = g^x h^r`, where `g` and `h` are generators of a cyclic group, and `r` is a random value chosen by the prover.
2. **Challenge Phase:** The verifier sends a challenge `c` to the prover. The challenge is a random value chosen from a finite field.
3. **Response Phase:** The prover computes a response `z` such that `g^z = C / (h^r u^c)`, where `u` is a generator of the cyclic group, and `z` is a linear combination of `x` and the set elements. The prover sends the response `z` to the verifier.

```bash
go version
```
The verifier can then check that `g^z = C / (h^r u^c)` holds, without learning any information about the value `x`.

### Installation
Implementation Details
----------------------

Clone the repository to your local machine:
Go-libzk13 provides an implementation of the ZK13 protocol in Go. The implementation uses the [bn256](https://godoc.org/github.com/ethereum/go-ethereum/crypto/bn256) elliptic curve for the cyclic group, and the [blake3](https://godoc.org/github.com/zeebo/blake3) hash function for hashing.

```bash
git clone https://github.com/twingdev/go-libzk13
cd go-libzk13
```
The implementation provides a simple API for generating and verifying zero-knowledge proofs of set membership. The API consists of the following functions:

### Running the Program
* `NewZK13(secretBaggage string, bits int) *ZK13`: Creates a new ZK13 instance with a prime number, generator, and hashed secret. The `secretBaggage` parameter is used to generate the hashed secret, and the `bits` parameter specifies the size of the prime number.
* `Prover(nonce *big.Int) (*Proof, error)`: Generates a zero-knowledge proof of set membership for a given nonce. The nonce is used to protect against replay attacks.
* `Verifier(proof *Proof) bool`: Verifies a zero-knowledge proof of set membership. Returns `true` if the proof is valid, and `false` otherwise.

To run the program and test different prime lengths:
Performance
-----------

```bash
go run main.go
```
Go-libzk13 is designed to be fast and efficient. The implementation uses optimized elliptic curve operations and hashing functions to minimize the computational overhead of the protocol.

### Profiling Performance
The following table shows the performance of the ZK13 protocol for different prime lengths:

To profile the program's performance for CPU and memory usage, run:
| Prime Length | Prover Time (ms) | Verifier Time (ms) |
| ------------ | --------------- | ------------------ |
| 512 | 0.4 | 0.1 |
| 1024 | 1.5 | 0.3 |
| 2048 | 6.1 | 1.2 |
| 2048 + 32 | 6.5 | 1.3 |

```bash
go build -o zk13
./zk13
```
The performance measurements were taken on an Intel Core i7-9750H CPU @ 2.60GHz.

Then, analyze the performance profiles using:
Contributing
------------

```bash
go tool pprof cpu.prof
go tool pprof mem.prof
```
Go-libzk13 is an open-source project, and contributions are welcome. If you would like to contribute to the project, please open a pull request with your proposed changes.

## Usage
License
-------

This project is intended for educational purposes and as a demonstration of implementing cryptographic protocols in Go. It showcases the use of zero-knowledge proofs with variable prime lengths for enhanced security.

## Contributing

Contributions are welcome! Please feel free to submit pull requests, report issues, or suggest improvements.

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Acknowledgments

- Thanks to the cryptographic community for the continuous development and research in the field of zero-knowledge proofs.
- This project utilizes the [Blake3 hashing algorithm](https://github.com/zeebo/blake3) for cryptographic hashing.
Go-libzk13 is licensed under the [MIT License](https://opensource.org/licenses/MIT).

Contact
-------

If you have any questions or comments about go-libzk13, please open an issue on GitHub, or contact the maintainer at [[email protected]](mailto:[email protected]).
Binary file modified cpu.prof
Binary file not shown.
Binary file added go-libzk13
Binary file not shown.
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ go 1.22

require (
github.com/davecgh/go-spew v1.1.1
github.com/stretchr/testify v1.9.0
github.com/zeebo/blake3 v0.2.3
)

require github.com/klauspost/cpuid/v2 v2.0.12 // indirect
require (
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ 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/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
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/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
80 changes: 70 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package main

import (
"fmt"
"github.com/genovatix/go-libzk13/zkp"
"log"
"math/big"
"net/http"
_ "net/http/pprof"
"os"
Expand All @@ -28,28 +30,86 @@ func main() {
}
defer pprof.StopCPUProfile()

// Run tests
err = runTests()
if err != nil {
log.Fatalf("Error running tests: %v", err)
}

// Memory profiling
mf, err := os.Create("mem.prof")
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
defer mf.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(mf); err != nil {
log.Fatal("could not write memory profile: ", err)
}

}

func runTests() error {
// Test different prime lengths
for _, bits := range []int{512, 1024, 2048, 2048 + 32} {
fmt.Printf("Testing with prime length: %d bits\n", bits)
zk13 := NewZK13("shared secret", bits) // Adjust NewZK13 to accept prime length as an argument
nonce := zk13.GenerateNonce() // Generate a nonce for replay attack protection
zk13 := zkp.NewZK13("shared secret", bits) // Adjust NewZK13 to accept prime length as an argument
nonce := zk13.GenerateNonce() // Generate a nonce for replay attack protection
proof, err := zk13.Prover(nonce)
if err != nil {
log.Fatalf("Error generating proof: %v", err)
return fmt.Errorf("error generating proof: %v", err)
}
isValid := zk13.Verifier(proof)
fmt.Printf("Verification with %d bits prime: %v\n", bits, isValid)
}

// Memory profiling
mf, err := os.Create("mem.prof")
// Run timing attack test
zk13 := zkp.NewZK13("shared secret", 2048) // Use a fixed prime length for timing attack test
nonce := zk13.GenerateNonce() // Generate a nonce for replay attack protection
proof, err := zk13.Prover(nonce)
if err != nil {
log.Fatal("could not create memory profile: ", err)
return fmt.Errorf("error generating proof: %v", err)
}
defer mf.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(mf); err != nil {
log.Fatal("could not write memory profile: ", err)
isValid := zk13.Verifier(proof)
if !isValid {
return fmt.Errorf("proof should be valid")
}

// Modify the proof and verify that it is invalid
proof.R.Add(proof.R, big.NewInt(1))
isValid = zk13.Verifier(proof)
if isValid {
return fmt.Errorf("proof should be invalid")
}

// Modify the nonce and verify that the proof is invalid
proof.Nonce.Add(proof.Nonce, big.NewInt(1))
isValid = zk13.Verifier(proof)
if isValid {
return fmt.Errorf("proof should be invalid")
}

// Run replay attack test
zk13 = zkp.NewZK13("shared secret", 2048) // Use a fixed prime length for replay attack test
nonce = zk13.GenerateNonce() // Generate a nonce for replay attack protection
proof, err = zk13.Prover(nonce)
if err != nil {
return fmt.Errorf("error generating proof: %v", err)
}
isValid = zk13.Verifier(proof)
if !isValid {
return fmt.Errorf("proof should be valid")
}

// Use the same nonce to generate another proof
proof2, err := zk13.Prover(nonce)
if err != nil {
return fmt.Errorf("error generating proof: %v", err)
}
isValid = zk13.Verifier(proof2)
if isValid {
return fmt.Errorf("proof should be invalid")
}

return nil
}
Binary file modified mem.prof
Binary file not shown.
1 change: 0 additions & 1 deletion zk13_test.go

This file was deleted.

45 changes: 15 additions & 30 deletions libzk13.go → zkp/libzk13.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package main
package zkp

import (
"crypto/rand"
"crypto/subtle"
"fmt"
"github.com/davecgh/go-spew/spew"
"github.com/zeebo/blake3"
Expand Down Expand Up @@ -73,18 +72,22 @@ func (z *ZK13) Prover(nonce *big.Int) (*Proof, error) {

// Verifier checks if the provided proof (r, P, nonce) is valid Verifier checks the proof using constant-time comparison
func (z *ZK13) Verifier(proof *Proof) bool {
V := new(big.Int).Exp(proof.R, z.Hs, z.p)
// Convert V and P to byte slices for comparison
VBytes := V.Bytes()
PBytes := proof.P.Bytes()
// Use subtle.ConstantTimeCompare to prevent timing attacks
if subtle.ConstantTimeCompare(VBytes, PBytes) != 1 {
// Calculate expected value of r
expectedR := new(big.Int).Exp(z.g, proof.Nonce, z.p)
expectedR.Mul(expectedR, new(big.Int).Exp(z.Hs, proof.R, z.p))
expectedR.Mod(expectedR, z.p)

// Check that r matches expected value
if proof.P.Cmp(expectedR) != 0 {
return false
}
// Check that the nonce is valid
if proof.Nonce.Cmp(big.NewInt(0)) <= 0 {

// Check that nonce is valid
if proof.Nonce.Cmp(big.NewInt(1)) <= 0 || proof.Nonce.Cmp(z.q) >= 0 {
return false
}

// Proof is valid
return true
}

Expand All @@ -107,8 +110,8 @@ func (z *ZK13) calculateF(k *big.Int) *big.Int {
return new(big.Int).Mod(new(big.Int).Mul(z.Hs, k), pMinusOne)
}

// calculateP calculates P = g^F mod p.
func (z *ZK13) calculateP(F *big.Int) *big.Int {
// CalculateP calculates P = g^F mod p.
func (z *ZK13) CalculateP(F *big.Int) *big.Int {
return new(big.Int).Exp(z.g, F, z.p)
}

Expand All @@ -117,12 +120,6 @@ func (z *ZK13) GenerateNonce() *big.Int {
return b
}

// Verify checks if the given P matches r^Hs mod p, validating the proof.
func Verify(r, Hs, P, p *big.Int) bool {
V := new(big.Int).Exp(r, Hs, p)
return V.Cmp(P) == 0
}

// randBigInt generates a random big integer within a specified range.
func randBigInt(max *big.Int) (*big.Int, error) {
n, err := rand.Int(rand.Reader, max)
Expand All @@ -132,18 +129,6 @@ func randBigInt(max *big.Int) (*big.Int, error) {
return n, nil
}

// SetupZK13Verifier simulates the verifier setup for demonstration purposes.
// In practice, the verifier should only need to verify the proof against known parameters, not generate them.
func SetupZK13Verifier(z *ZK13) *Verifier {
v := &Verifier{}
k, _ := randBigInt(big.NewInt(PubKeyRange)) // Better error handling should be added
v.k = k
v.r = z.calculateR(k)
v.F = z.calculateF(k)
v.P = z.calculateP(v.F)
return v
}

type Verifier struct {
k, r, F, P *big.Int
}
Expand Down
2 changes: 1 addition & 1 deletion security.go → zkp/security.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package zkp

import "math/big"

Expand Down

0 comments on commit 38f78bd

Please sign in to comment.