mirror of
https://github.com/luxfi/threshold.git
synced 2026-10-30 15:15:53 +00:00
149 lines
2.9 KiB
Go
149 lines
2.9 KiB
Go
package zkprm
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"io"
|
|
"math/big"
|
|
|
|
"github.com/cronokirby/saferith"
|
|
"github.com/luxfi/threshold/internal/params"
|
|
"github.com/luxfi/threshold/pkg/hash"
|
|
"github.com/luxfi/threshold/pkg/math/arith"
|
|
"github.com/luxfi/threshold/pkg/math/sample"
|
|
"github.com/luxfi/threshold/pkg/pedersen"
|
|
"github.com/luxfi/threshold/pkg/pool"
|
|
)
|
|
|
|
type Public struct {
|
|
Aux *pedersen.Parameters
|
|
}
|
|
type Private struct {
|
|
Lambda, Phi, P, Q *saferith.Nat
|
|
}
|
|
|
|
type Proof struct {
|
|
As, Zs [params.StatParam]*big.Int
|
|
}
|
|
|
|
func (p *Proof) IsValid(public Public) bool {
|
|
if p == nil {
|
|
return false
|
|
}
|
|
|
|
if !arith.IsValidBigModN(public.Aux.N().Big(), append(p.As[:], p.Zs[:]...)...) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// NewProof generates a proof that:
|
|
// s = t^lambda (mod N).
|
|
func NewProof(private Private, hash *hash.Hash, public Public, pl *pool.Pool) *Proof {
|
|
lambda := private.Lambda
|
|
phi := saferith.ModulusFromNat(private.Phi)
|
|
|
|
n := arith.ModulusFromFactors(private.P, private.Q)
|
|
|
|
var (
|
|
as [params.StatParam]*saferith.Nat
|
|
As [params.StatParam]*big.Int
|
|
)
|
|
lockedRand := pool.NewLockedReader(rand.Reader)
|
|
pl.Parallelize(params.StatParam, func(i int) interface{} {
|
|
// aᵢ ∈ mod ϕ(N)
|
|
as[i] = sample.ModN(lockedRand, phi)
|
|
|
|
// Aᵢ = tᵃ mod N
|
|
As[i] = n.Exp(public.Aux.T(), as[i]).Big()
|
|
|
|
return nil
|
|
})
|
|
|
|
es, _ := challenge(hash, public, As)
|
|
// Modular addition is not expensive enough to warrant parallelizing
|
|
var Zs [params.StatParam]*big.Int
|
|
for i := 0; i < params.StatParam; i++ {
|
|
z := as[i]
|
|
// The challenge is public, so branching is ok
|
|
if es[i] {
|
|
z.ModAdd(z, lambda, phi)
|
|
}
|
|
Zs[i] = z.Big()
|
|
}
|
|
|
|
return &Proof{
|
|
As: As,
|
|
Zs: Zs,
|
|
}
|
|
}
|
|
|
|
func (p *Proof) Verify(public Public, hash *hash.Hash, pl *pool.Pool) bool {
|
|
if p == nil {
|
|
return false
|
|
}
|
|
if err := pedersen.ValidateParameters(public.Aux.N(), public.Aux.S(), public.Aux.T()); err != nil {
|
|
return false
|
|
}
|
|
|
|
n, s, t := public.Aux.N().Big(), public.Aux.S().Big(), public.Aux.T().Big()
|
|
|
|
es, err := challenge(hash, public, p.As)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
one := big.NewInt(1)
|
|
verifications := pl.Parallelize(params.StatParam, func(i int) interface{} {
|
|
var lhs, rhs big.Int
|
|
z := p.Zs[i]
|
|
a := p.As[i]
|
|
|
|
if !arith.IsValidBigModN(n, a, z) {
|
|
return false
|
|
}
|
|
|
|
if a.Cmp(one) == 0 {
|
|
return false
|
|
}
|
|
|
|
lhs.Exp(t, z, n)
|
|
if es[i] {
|
|
rhs.Mul(a, s)
|
|
rhs.Mod(&rhs, n)
|
|
} else {
|
|
rhs.Set(a)
|
|
}
|
|
|
|
if lhs.Cmp(&rhs) != 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
})
|
|
for i := 0; i < len(verifications); i++ {
|
|
ok, _ := verifications[i].(bool)
|
|
if !ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func challenge(hash *hash.Hash, public Public, A [params.StatParam]*big.Int) (es []bool, err error) {
|
|
err = hash.WriteAny(public.Aux)
|
|
for _, a := range A {
|
|
_ = hash.WriteAny(a)
|
|
}
|
|
|
|
tmpBytes := make([]byte, params.StatParam)
|
|
_, _ = io.ReadFull(hash.Digest(), tmpBytes)
|
|
|
|
es = make([]bool, params.StatParam)
|
|
for i := range es {
|
|
b := (tmpBytes[i] & 1) == 1
|
|
es[i] = b
|
|
}
|
|
|
|
return
|
|
}
|