mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-25 16:15:17 +00:00
519 lines
12 KiB
Go
519 lines
12 KiB
Go
//
|
||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||
//
|
||
// SPDX-License-Identifier: Apache-2.0
|
||
//
|
||
|
||
package accumulator
|
||
|
||
import (
|
||
"bytes"
|
||
crand "crypto/rand"
|
||
"errors"
|
||
"fmt"
|
||
|
||
"git.sr.ht/~sircmpwn/go-bare"
|
||
|
||
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
|
||
)
|
||
|
||
type proofParamsMarshal struct {
|
||
X []byte `bare:"x"`
|
||
Y []byte `bare:"y"`
|
||
Z []byte `bare:"z"`
|
||
Curve string `bare:"curve"`
|
||
}
|
||
|
||
// ProofParams contains four distinct public generators of G1 - X, Y, Z
|
||
type ProofParams struct {
|
||
x, y, z curves.Point
|
||
}
|
||
|
||
// New samples X, Y, Z, K
|
||
func (p *ProofParams) New(curve *curves.PairingCurve, pk *PublicKey, entropy []byte) (*ProofParams, error) {
|
||
pkBytes, err := pk.MarshalBinary()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
prefix := bytes.Repeat([]byte{0xFF}, 32)
|
||
data := append(prefix, entropy...)
|
||
data = append(data, pkBytes...)
|
||
p.z = curve.Scalar.Point().Hash(data)
|
||
|
||
data[0] = 0xFE
|
||
p.y = curve.Scalar.Point().Hash(data)
|
||
|
||
data[0] = 0xFD
|
||
p.x = curve.Scalar.Point().Hash(data)
|
||
|
||
return p, nil
|
||
}
|
||
|
||
// MarshalBinary converts ProofParams to bytes
|
||
func (p *ProofParams) MarshalBinary() ([]byte, error) {
|
||
if p.x == nil || p.y == nil || p.z == nil {
|
||
return nil, fmt.Errorf("some value x, y, or z is nil")
|
||
}
|
||
tv := &proofParamsMarshal{
|
||
X: p.x.ToAffineCompressed(),
|
||
Y: p.y.ToAffineCompressed(),
|
||
Z: p.z.ToAffineCompressed(),
|
||
Curve: p.x.CurveName(),
|
||
}
|
||
return bare.Marshal(tv)
|
||
}
|
||
|
||
// UnmarshalBinary converts bytes to ProofParams
|
||
func (p *ProofParams) UnmarshalBinary(data []byte) error {
|
||
if data == nil {
|
||
return fmt.Errorf("expected non-zero byte sequence")
|
||
}
|
||
tv := new(proofParamsMarshal)
|
||
err := bare.Unmarshal(data, tv)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
curve := curves.GetCurveByName(tv.Curve)
|
||
if curve == nil {
|
||
return fmt.Errorf("invalid curve")
|
||
}
|
||
x, err := curve.NewIdentityPoint().FromAffineCompressed(tv.X)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
y, err := curve.NewIdentityPoint().FromAffineCompressed(tv.Y)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
z, err := curve.NewIdentityPoint().FromAffineCompressed(tv.Z)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
p.x = x
|
||
p.y = y
|
||
p.z = z
|
||
return nil
|
||
}
|
||
|
||
// MembershipProofCommitting contains value computed in Proof of knowledge and
|
||
// Blinding phases as described in section 7 of https://eprint.iacr.org/2020/777.pdf
|
||
type MembershipProofCommitting struct {
|
||
eC curves.Point
|
||
tSigma curves.Point
|
||
tRho curves.Point
|
||
deltaSigma curves.Scalar
|
||
deltaRho curves.Scalar
|
||
blindingFactor curves.Scalar
|
||
rSigma curves.Scalar
|
||
rRho curves.Scalar
|
||
rDeltaSigma curves.Scalar
|
||
rDeltaRho curves.Scalar
|
||
sigma curves.Scalar
|
||
rho curves.Scalar
|
||
capRSigma curves.Point
|
||
capRRho curves.Point
|
||
capRDeltaSigma curves.Point
|
||
capRDeltaRho curves.Point
|
||
capRE curves.Scalar
|
||
accumulator curves.Point
|
||
witnessValue curves.Scalar
|
||
xG1 curves.Point
|
||
yG1 curves.Point
|
||
zG1 curves.Point
|
||
}
|
||
|
||
// New initiates values of MembershipProofCommitting
|
||
func (mpc *MembershipProofCommitting) New(
|
||
witness *MembershipWitness,
|
||
acc *Accumulator,
|
||
pp *ProofParams,
|
||
pk *PublicKey,
|
||
) (*MembershipProofCommitting, error) {
|
||
// Randomly select σ, ρ
|
||
sigma := witness.y.Random(crand.Reader)
|
||
rho := witness.y.Random(crand.Reader)
|
||
|
||
// E_C = C + (σ + ρ)Z
|
||
t := sigma
|
||
t = t.Add(rho)
|
||
eC := pp.z
|
||
eC = eC.Mul(t)
|
||
eC = eC.Add(witness.c)
|
||
|
||
// T_σ = σX
|
||
tSigma := pp.x
|
||
tSigma = tSigma.Mul(sigma)
|
||
|
||
// T_ρ = ρY
|
||
tRho := pp.y
|
||
tRho = tRho.Mul(rho)
|
||
|
||
// δ_σ = yσ
|
||
deltaSigma := witness.y
|
||
deltaSigma = deltaSigma.Mul(sigma)
|
||
|
||
// δ_ρ = yρ
|
||
deltaRho := witness.y
|
||
deltaRho = deltaRho.Mul(rho)
|
||
|
||
// Randomly pick r_σ,r_ρ,r_δσ,r_δρ
|
||
rY := witness.y.Random(crand.Reader)
|
||
rSigma := witness.y.Random(crand.Reader)
|
||
rRho := witness.y.Random(crand.Reader)
|
||
rDeltaSigma := witness.y.Random(crand.Reader)
|
||
rDeltaRho := witness.y.Random(crand.Reader)
|
||
|
||
// R_σ = r_σ X
|
||
capRSigma := pp.x
|
||
capRSigma = capRSigma.Mul(rSigma)
|
||
|
||
// R_ρ = ρY
|
||
capRRho := pp.y
|
||
capRRho = capRRho.Mul(rRho)
|
||
|
||
// R_δσ = r_y T_σ - r_δσ X
|
||
negX := pp.x
|
||
negX = negX.Neg()
|
||
capRDeltaSigma := tSigma.Mul(rY)
|
||
capRDeltaSigma = capRDeltaSigma.Add(negX.Mul(rDeltaSigma))
|
||
|
||
// R_δρ = r_y T_ρ - r_δρ Y
|
||
negY := pp.y
|
||
negY = negY.Neg()
|
||
capRDeltaRho := tRho.Mul(rY)
|
||
capRDeltaRho = capRDeltaRho.Add(negY.Mul(rDeltaRho))
|
||
|
||
// P~
|
||
g2 := pk.value.Generator()
|
||
|
||
// -r_δσ - r_δρ
|
||
exp := rDeltaSigma
|
||
exp = exp.Add(rDeltaRho)
|
||
exp = exp.Neg()
|
||
|
||
// -r_σ - r_ρ
|
||
exp2 := rSigma
|
||
exp2 = exp2.Add(rRho)
|
||
exp2 = exp2.Neg()
|
||
|
||
// rY * eC
|
||
rYeC := eC.Mul(rY)
|
||
|
||
// (-r_δσ - r_δρ)*Z
|
||
expZ := pp.z.Mul(exp)
|
||
|
||
// (-r_σ - r_ρ)*Z
|
||
exp2Z := pp.z.Mul(exp2)
|
||
|
||
// Prepare
|
||
rYeCPrep, ok := rYeC.(curves.PairingPoint)
|
||
if !ok {
|
||
return nil, errors.New("incorrect type conversion")
|
||
}
|
||
g2Prep, ok := g2.(curves.PairingPoint)
|
||
if !ok {
|
||
return nil, errors.New("incorrect type conversion")
|
||
}
|
||
expZPrep, ok := expZ.(curves.PairingPoint)
|
||
if !ok {
|
||
return nil, errors.New("incorrect type conversion")
|
||
}
|
||
exp2ZPrep, ok := exp2Z.(curves.PairingPoint)
|
||
if !ok {
|
||
return nil, errors.New("incorrect type conversion")
|
||
}
|
||
pkPrep := pk.value
|
||
|
||
// Pairing
|
||
capRE := g2Prep.MultiPairing(rYeCPrep, g2Prep, expZPrep, g2Prep, exp2ZPrep, pkPrep)
|
||
|
||
return &MembershipProofCommitting{
|
||
eC,
|
||
tSigma,
|
||
tRho,
|
||
deltaSigma,
|
||
deltaRho,
|
||
rY,
|
||
rSigma,
|
||
rRho,
|
||
rDeltaSigma,
|
||
rDeltaRho,
|
||
sigma,
|
||
rho,
|
||
capRSigma,
|
||
capRRho,
|
||
capRDeltaSigma,
|
||
capRDeltaRho,
|
||
capRE,
|
||
acc.value,
|
||
witness.y,
|
||
pp.x,
|
||
pp.y,
|
||
pp.z,
|
||
}, nil
|
||
}
|
||
|
||
// GetChallenge returns bytes that need to be hashed for generating challenge.
|
||
// V || Ec || T_sigma || T_rho || R_E || R_sigma || R_rho || R_delta_sigma || R_delta_rho
|
||
func (mpc MembershipProofCommitting) GetChallengeBytes() []byte {
|
||
res := mpc.accumulator.ToAffineCompressed()
|
||
res = append(res, mpc.eC.ToAffineCompressed()...)
|
||
res = append(res, mpc.tSigma.ToAffineCompressed()...)
|
||
res = append(res, mpc.tRho.ToAffineCompressed()...)
|
||
res = append(res, mpc.capRE.Bytes()...)
|
||
res = append(res, mpc.capRSigma.ToAffineCompressed()...)
|
||
res = append(res, mpc.capRRho.ToAffineCompressed()...)
|
||
res = append(res, mpc.capRDeltaSigma.ToAffineCompressed()...)
|
||
res = append(res, mpc.capRDeltaRho.ToAffineCompressed()...)
|
||
return res
|
||
}
|
||
|
||
// GenProof computes the s values for Fiat-Shamir and return the actual
|
||
// proof to be sent to the verifier given the challenge c.
|
||
func (mpc *MembershipProofCommitting) GenProof(c curves.Scalar) *MembershipProof {
|
||
// s_y = r_y + c*y
|
||
sY := schnorr(mpc.blindingFactor, mpc.witnessValue, c)
|
||
// s_σ = r_σ + c*σ
|
||
sSigma := schnorr(mpc.rSigma, mpc.sigma, c)
|
||
// s_ρ = r_ρ + c*ρ
|
||
sRho := schnorr(mpc.rRho, mpc.rho, c)
|
||
// s_δσ = rδσ + c*δ_σ
|
||
sDeltaSigma := schnorr(mpc.rDeltaSigma, mpc.deltaSigma, c)
|
||
// s_δρ = rδρ + c*δ_ρ
|
||
sDeltaRho := schnorr(mpc.rDeltaRho, mpc.deltaRho, c)
|
||
|
||
return &MembershipProof{
|
||
mpc.eC,
|
||
mpc.tSigma,
|
||
mpc.tRho,
|
||
sSigma,
|
||
sRho,
|
||
sDeltaSigma,
|
||
sDeltaRho,
|
||
sY,
|
||
}
|
||
}
|
||
|
||
func schnorr(r, v, challenge curves.Scalar) curves.Scalar {
|
||
res := v
|
||
res = res.Mul(challenge)
|
||
res = res.Add(r)
|
||
return res
|
||
}
|
||
|
||
type membershipProofMarshal struct {
|
||
EC []byte `bare:"e_c"`
|
||
TSigma []byte `bare:"t_sigma"`
|
||
TRho []byte `bare:"t_rho"`
|
||
SSigma []byte `bare:"s_sigma"`
|
||
SRho []byte `bare:"s_rho"`
|
||
SDeltaSigma []byte `bare:"s_delta_sigma"`
|
||
SDeltaRho []byte `bare:"s_delta_rho"`
|
||
SY []byte `bare:"s_y"`
|
||
Curve string `bare:"curve"`
|
||
}
|
||
|
||
// MembershipProof contains values in the proof to be verified
|
||
type MembershipProof struct {
|
||
eC curves.Point
|
||
tSigma curves.Point
|
||
tRho curves.Point
|
||
sSigma curves.Scalar
|
||
sRho curves.Scalar
|
||
sDeltaSigma curves.Scalar
|
||
sDeltaRho curves.Scalar
|
||
sY curves.Scalar
|
||
}
|
||
|
||
// Finalize computes values in the proof to be verified.
|
||
func (mp *MembershipProof) Finalize(acc *Accumulator, pp *ProofParams, pk *PublicKey, challenge curves.Scalar) (*MembershipProofFinal, error) {
|
||
// R_σ = s_δ X + c T_σ
|
||
negTSigma := mp.tSigma
|
||
negTSigma = negTSigma.Neg()
|
||
capRSigma := pp.x.Mul(mp.sSigma)
|
||
capRSigma = capRSigma.Add(negTSigma.Mul(challenge))
|
||
|
||
// R_ρ = s_ρ Y + c T_ρ
|
||
negTRho := mp.tRho
|
||
negTRho = negTRho.Neg()
|
||
capRRho := pp.y.Mul(mp.sRho)
|
||
capRRho = capRRho.Add(negTRho.Mul(challenge))
|
||
|
||
// R_δσ = s_y T_σ - s_δσ X
|
||
negX := pp.x
|
||
negX = negX.Neg()
|
||
capRDeltaSigma := mp.tSigma.Mul(mp.sY)
|
||
capRDeltaSigma = capRDeltaSigma.Add(negX.Mul(mp.sDeltaSigma))
|
||
|
||
// R_δρ = s_y T_ρ - s_δρ Y
|
||
negY := pp.y
|
||
negY = negY.Neg()
|
||
capRDeltaRho := mp.tRho.Mul(mp.sY)
|
||
capRDeltaRho = capRDeltaRho.Add(negY.Mul(mp.sDeltaRho))
|
||
|
||
// tildeP
|
||
g2 := pk.value.Generator()
|
||
|
||
// Compute capRE, the pairing
|
||
// E_c * s_y
|
||
eCsY := mp.eC.Mul(mp.sY)
|
||
|
||
// (-s_delta_sigma - s_delta_rho) * Z
|
||
exp := mp.sDeltaSigma
|
||
exp = exp.Add(mp.sDeltaRho)
|
||
exp = exp.Neg()
|
||
expZ := pp.z.Mul(exp)
|
||
|
||
// (-c) * V
|
||
exp = challenge.Neg()
|
||
expV := acc.value.Mul(exp)
|
||
|
||
// E_c * s_y + (-s_delta_sigma - s_delta_rho) * Z + (-c) * V
|
||
lhs := eCsY.Add(expZ).Add(expV)
|
||
|
||
// (-s_sigma - s_rho) * Z
|
||
exp = mp.sSigma
|
||
exp = exp.Add(mp.sRho)
|
||
exp = exp.Neg()
|
||
expZ2 := pp.z.Mul(exp)
|
||
|
||
// E_c * c
|
||
cEc := mp.eC.Mul(challenge)
|
||
|
||
// (-s_sigma - s_rho) * Z + E_c * c
|
||
rhs := cEc.Add(expZ2)
|
||
|
||
// Prepare
|
||
lhsPrep, ok := lhs.(curves.PairingPoint)
|
||
if !ok {
|
||
return nil, errors.New("incorrect type conversion")
|
||
}
|
||
g2Prep, ok := g2.(curves.PairingPoint)
|
||
if !ok {
|
||
return nil, errors.New("incorrect type conversion")
|
||
}
|
||
rhsPrep, ok := rhs.(curves.PairingPoint)
|
||
if !ok {
|
||
return nil, errors.New("incorrect type conversion")
|
||
}
|
||
pkPrep := pk.value
|
||
|
||
// capRE
|
||
capRE := g2Prep.MultiPairing(lhsPrep, g2Prep, rhsPrep, pkPrep)
|
||
|
||
return &MembershipProofFinal{
|
||
acc.value,
|
||
mp.eC,
|
||
mp.tSigma,
|
||
mp.tRho,
|
||
capRE,
|
||
capRSigma,
|
||
capRRho,
|
||
capRDeltaSigma,
|
||
capRDeltaRho,
|
||
}, nil
|
||
}
|
||
|
||
// MarshalBinary converts MembershipProof to bytes
|
||
func (mp MembershipProof) MarshalBinary() ([]byte, error) {
|
||
tv := &membershipProofMarshal{
|
||
EC: mp.eC.ToAffineCompressed(),
|
||
TSigma: mp.tSigma.ToAffineCompressed(),
|
||
TRho: mp.tRho.ToAffineCompressed(),
|
||
SSigma: mp.sSigma.Bytes(),
|
||
SRho: mp.sRho.Bytes(),
|
||
SDeltaSigma: mp.sDeltaSigma.Bytes(),
|
||
SDeltaRho: mp.sDeltaRho.Bytes(),
|
||
SY: mp.sY.Bytes(),
|
||
Curve: mp.eC.CurveName(),
|
||
}
|
||
return bare.Marshal(tv)
|
||
}
|
||
|
||
// UnmarshalBinary converts bytes to MembershipProof
|
||
func (mp *MembershipProof) UnmarshalBinary(data []byte) error {
|
||
if data == nil {
|
||
return fmt.Errorf("expected non-zero byte sequence")
|
||
}
|
||
tv := new(membershipProofMarshal)
|
||
err := bare.Unmarshal(data, tv)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
curve := curves.GetCurveByName(tv.Curve)
|
||
if curve == nil {
|
||
return fmt.Errorf("invalid curve")
|
||
}
|
||
eC, err := curve.NewIdentityPoint().FromAffineCompressed(tv.EC)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
tSigma, err := curve.NewIdentityPoint().FromAffineCompressed(tv.TSigma)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
tRho, err := curve.NewIdentityPoint().FromAffineCompressed(tv.TRho)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sSigma, err := curve.NewScalar().SetBytes(tv.SSigma)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sRho, err := curve.NewScalar().SetBytes(tv.SRho)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sDeltaSigma, err := curve.NewScalar().SetBytes(tv.SDeltaSigma)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sDeltaRho, err := curve.NewScalar().SetBytes(tv.SDeltaRho)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
sY, err := curve.NewScalar().SetBytes(tv.SY)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
mp.eC = eC
|
||
mp.tSigma = tSigma
|
||
mp.tRho = tRho
|
||
mp.sSigma = sSigma
|
||
mp.sRho = sRho
|
||
mp.sDeltaSigma = sDeltaSigma
|
||
mp.sDeltaRho = sDeltaRho
|
||
mp.sY = sY
|
||
|
||
return nil
|
||
}
|
||
|
||
// MembershipProofFinal contains values that are input to Fiat-Shamir Heuristic
|
||
type MembershipProofFinal struct {
|
||
accumulator curves.Point
|
||
eC curves.Point
|
||
tSigma curves.Point
|
||
tRho curves.Point
|
||
capRE curves.Scalar
|
||
capRSigma curves.Point
|
||
capRRho curves.Point
|
||
capRDeltaSigma curves.Point
|
||
capRDeltaRho curves.Point
|
||
}
|
||
|
||
// GetChallenge computes Fiat-Shamir Heuristic taking input values of MembershipProofFinal
|
||
func (m MembershipProofFinal) GetChallenge(curve *curves.PairingCurve) curves.Scalar {
|
||
res := m.accumulator.ToAffineCompressed()
|
||
res = append(res, m.eC.ToAffineCompressed()...)
|
||
res = append(res, m.tSigma.ToAffineCompressed()...)
|
||
res = append(res, m.tRho.ToAffineCompressed()...)
|
||
res = append(res, m.capRE.Bytes()...)
|
||
res = append(res, m.capRSigma.ToAffineCompressed()...)
|
||
res = append(res, m.capRRho.ToAffineCompressed()...)
|
||
res = append(res, m.capRDeltaSigma.ToAffineCompressed()...)
|
||
res = append(res, m.capRDeltaRho.ToAffineCompressed()...)
|
||
challenge := curve.Scalar.Hash(res)
|
||
return challenge
|
||
}
|