mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-26 16:45:18 +00:00
114 lines
3.3 KiB
Go
114 lines
3.3 KiB
Go
//
|
|
// Copyright Coinbase, Inc. All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package v0
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
|
|
)
|
|
|
|
type Schnorr struct {
|
|
params *Params
|
|
Pub *curves.EcPoint // this is the public point.
|
|
C *big.Int
|
|
S *big.Int
|
|
}
|
|
|
|
func (proof *Schnorr) Prove(x *big.Int) error {
|
|
// assumes that params, Base, and pub are already populated. populates the fields c and s...
|
|
var err error
|
|
proof.Pub, err = curves.NewScalarBaseMult(proof.params.Curve, x) // populate the statement
|
|
if err != nil {
|
|
return err
|
|
}
|
|
k, err := proof.params.Scalar.Random()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
random, err := curves.NewScalarBaseMult(proof.params.Curve, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hash := sha256.New()
|
|
if _, err = hash.Write(proof.Pub.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
if _, err = hash.Write(random.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
// do we need to hash anything further here...? maybe some kind of session token or something?
|
|
proof.C = new(big.Int).SetBytes(hash.Sum(nil))
|
|
proof.S = proof.params.Scalar.Add(proof.params.Scalar.Mul(proof.C, x), k)
|
|
return nil
|
|
}
|
|
|
|
func (proof *Schnorr) Verify() error {
|
|
gs, err := curves.NewScalarBaseMult(proof.params.Curve, proof.S)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
xc, err := proof.Pub.ScalarMult(proof.params.Scalar.Neg(proof.C))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
random, err := gs.Add(xc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hash := sha256.New()
|
|
if _, err = hash.Write(proof.Pub.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
if _, err = hash.Write(random.Bytes()); err != nil {
|
|
return err
|
|
}
|
|
if subtle.ConstantTimeCompare(proof.params.Scalar.Bytes(proof.C), hash.Sum(nil)) != 1 {
|
|
return fmt.Errorf("Schnorr verification failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// this "commits to" a schnorr proof which is later revealed; see Functionality 7.
|
|
// it mutates `st` by adding a proof to it, and then also returns the commitment to the proof.
|
|
func (proof *Schnorr) ProveCommit(x *big.Int) ([]byte, error) {
|
|
// it remains to be seen why this is actually necessary in the paper. it's unsual.
|
|
// i suspect it is some "abstract nonsense" necessary for the security proof to go through.
|
|
// in the implementation (see https://gitlab.com/neucrypt/mpecdsa/-/blob/release/src/zkpok.rs),
|
|
// they implement this just by hashing the schnorr proof; we do the same (using c, s as the proof instead of K, s).
|
|
// it's a bit weird that no randomness is used, but "hiding" shouldn't really be an issue, as the proof is random.
|
|
// confirm that this actually matches how they implement it, and/or is secure. i don't think it should be an issue.
|
|
if err := proof.Prove(x); err != nil {
|
|
return nil, err
|
|
}
|
|
hash := sha256.New()
|
|
if _, err := hash.Write(proof.params.Scalar.Bytes(proof.C)); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := hash.Write(proof.params.Scalar.Bytes(proof.S)); err != nil {
|
|
return nil, err
|
|
}
|
|
return hash.Sum(nil), nil
|
|
}
|
|
|
|
func (proof *Schnorr) DecommitVerify(com []byte) error {
|
|
hash := sha256.New()
|
|
if _, err := hash.Write(proof.params.Scalar.Bytes(proof.C)); err != nil {
|
|
return err
|
|
}
|
|
if _, err := hash.Write(proof.params.Scalar.Bytes(proof.S)); err != nil {
|
|
return err
|
|
}
|
|
if subtle.ConstantTimeCompare(hash.Sum(nil), com) != 1 {
|
|
return fmt.Errorf("initial hash decommitment failed")
|
|
}
|
|
return proof.Verify()
|
|
}
|