No commit suggestions generated

This commit is contained in:
Prad Nukala
2025-10-09 15:10:39 -04:00
commit a934caa7d3
323 changed files with 98121 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
"errors"
"fmt"
"github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/signatures/common"
)
// BlindSignature is a BBS+ blind signature
// structurally identical to `Signature` but
// is used to help avoid misuse and confusion.
//
// 1 or more message have been hidden by the
// potential signature holder so the signer
// only knows a subset of the messages to be signed
type BlindSignature struct {
a curves.PairingPoint
e, s curves.Scalar
}
// Init creates an empty signature to a specific curve
// which should be followed by UnmarshalBinary
func (sig *BlindSignature) Init(curve *curves.PairingCurve) *BlindSignature {
sig.a = curve.NewG1IdentityPoint()
sig.e = curve.NewScalar()
sig.s = curve.NewScalar()
return sig
}
func (sig BlindSignature) MarshalBinary() ([]byte, error) {
out := append(sig.a.ToAffineCompressed(), sig.e.Bytes()...)
out = append(out, sig.s.Bytes()...)
return out, nil
}
func (sig *BlindSignature) UnmarshalBinary(data []byte) error {
pointLength := len(sig.a.ToAffineCompressed())
scalarLength := len(sig.s.Bytes())
expectedLength := pointLength + scalarLength*2
if len(data) != expectedLength {
return fmt.Errorf("invalid byte sequence")
}
a, err := sig.a.FromAffineCompressed(data[:pointLength])
if err != nil {
return err
}
e, err := sig.e.SetBytes(data[pointLength:(pointLength + scalarLength)])
if err != nil {
return err
}
s, err := sig.s.SetBytes(data[(pointLength + scalarLength):])
if err != nil {
return err
}
var ok bool
sig.a, ok = a.(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
sig.e = e
sig.s = s
return nil
}
func (sig BlindSignature) ToUnblinded(blinder common.SignatureBlinding) *Signature {
return &Signature{
a: sig.a,
e: sig.e,
s: sig.s.Add(blinder),
}
}
+271
View File
@@ -0,0 +1,271 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
"fmt"
"io"
"sort"
"github.com/gtank/merlin"
"golang.org/x/crypto/sha3"
"github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/internal"
"github.com/sonr-io/sonr/crypto/signatures/common"
)
// BlindSignatureContext contains the data used for computing
// a blind signature and verifying proof of hidden messages from
// a future signature holder. A potential holder commits to messages
// that the signer will not know during the signing process
// rendering them hidden, but requires the holder to
// prove knowledge of those messages so a malicious party
// doesn't add just random data from anywhere.
type BlindSignatureContext struct {
// The blinded signature commitment
commitment common.Commitment
// The challenge hash for the Fiat-Shamir heuristic
challenge curves.Scalar
// The proofs of hidden messages.
proofs []curves.Scalar
}
// NewBlindSignatureContext creates the data needed to
// send to a signer to complete a blinded signature
// `msgs` is an index to message map where the index
// corresponds to the index in `generators`
// msgs are hidden from the signer but can also be empty
// if no messages are hidden but a blind signature is still desired
// because the signer should have no knowledge of the signature
func NewBlindSignatureContext(
curve *curves.PairingCurve,
msgs map[int]curves.Scalar,
generators *MessageGenerators,
nonce common.Nonce,
reader io.Reader,
) (*BlindSignatureContext, common.SignatureBlinding, error) {
if curve == nil || generators == nil || nonce == nil || reader == nil {
return nil, nil, internal.ErrNilArguments
}
points := make([]curves.Point, 0)
secrets := make([]curves.Scalar, 0)
committing := common.NewProofCommittedBuilder(&curves.Curve{
Scalar: curve.Scalar,
Point: curve.Scalar.Point().(curves.PairingPoint).OtherGroup(),
Name: curve.Name,
})
// C = h0^blinding_factor*h_i^m_i.....
for i, m := range msgs {
if i > generators.length || i < 0 {
return nil, nil, fmt.Errorf("invalid index")
}
secrets = append(secrets, m)
pt := generators.Get(i + 1)
points = append(points, pt)
err := committing.CommitRandom(pt, reader)
if err != nil {
return nil, nil, err
}
}
blinding, ok := curve.Scalar.Random(reader).(common.SignatureBlinding)
if !ok {
return nil, nil, fmt.Errorf("unable to create signature blinding")
}
secrets = append(secrets, blinding)
h0 := generators.Get(0)
points = append(points, h0)
err := committing.CommitRandom(h0, reader)
if err != nil {
return nil, nil, err
}
// Create a random commitment, compute challenges and response.
// The proof of knowledge consists of a commitment and responses
// Holder and signer engage in a proof of knowledge for `commitment`
commitment := curve.Scalar.Point().(curves.PairingPoint).OtherGroup().
SumOfProducts(points, secrets)
transcript := merlin.NewTranscript("new blind signature")
transcript.AppendMessage([]byte("random commitment"), committing.GetChallengeContribution())
transcript.AppendMessage([]byte("blind commitment"), commitment.ToAffineCompressed())
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm := transcript.ExtractBytes([]byte("blind signature context challenge"), 64)
challenge, err := curve.Scalar.SetBytesWide(okm)
if err != nil {
return nil, nil, err
}
proofs, err := committing.GenerateProof(challenge, secrets)
if err != nil {
return nil, nil, err
}
return &BlindSignatureContext{
commitment: commitment,
challenge: challenge,
proofs: proofs,
}, blinding, nil
}
func (bsc *BlindSignatureContext) Init(curve *curves.PairingCurve) *BlindSignatureContext {
bsc.challenge = curve.NewScalar()
bsc.commitment = curve.Scalar.Point().(curves.PairingPoint).OtherGroup()
bsc.proofs = make([]curves.Scalar, 0)
return bsc
}
// MarshalBinary store the generators as a sequence of bytes
// where each point is compressed.
// Needs (N + 1) * ScalarSize + PointSize
func (bsc BlindSignatureContext) MarshalBinary() ([]byte, error) {
buffer := append(bsc.commitment.ToAffineCompressed(), bsc.challenge.Bytes()...)
for _, p := range bsc.proofs {
buffer = append(buffer, p.Bytes()...)
}
return buffer, nil
}
func (bsc *BlindSignatureContext) UnmarshalBinary(in []byte) error {
scSize := len(bsc.challenge.Bytes())
ptSize := len(bsc.commitment.ToAffineCompressed())
if len(in) < scSize*2+ptSize {
return fmt.Errorf("insufficient number of bytes")
}
if (len(in)-ptSize)%scSize != 0 {
return fmt.Errorf("invalid byte sequence")
}
commitment, err := bsc.commitment.FromAffineCompressed(in[:ptSize])
if err != nil {
return err
}
challenge, err := bsc.challenge.SetBytes(in[ptSize:(ptSize + scSize)])
if err != nil {
return err
}
nProofs := ((len(in) - ptSize) / scSize) - 1
proofs := make([]curves.Scalar, nProofs)
for i := 0; i < nProofs; i++ {
proofs[i], err = bsc.challenge.SetBytes(in[(ptSize + (i+1)*scSize):(ptSize + (i+2)*scSize)])
if err != nil {
return err
}
}
bsc.commitment = commitment
bsc.challenge = challenge
bsc.proofs = proofs
return nil
}
// Verify validates a proof of hidden messages
func (bsc BlindSignatureContext) Verify(
knownMsgs []int,
generators *MessageGenerators,
nonce common.Nonce,
) error {
known := make(map[int]bool, len(knownMsgs))
for _, i := range knownMsgs {
if i > generators.length {
return fmt.Errorf("invalid message index")
}
known[i] = true
}
points := make([]curves.Point, 0)
for i := 0; i < generators.length; i++ {
if _, contains := known[i]; !contains {
points = append(points, generators.Get(i+1))
}
}
points = append(points, generators.Get(0), bsc.commitment)
scalars := append(bsc.proofs, bsc.challenge.Neg())
commitment := points[0].SumOfProducts(points, scalars)
transcript := merlin.NewTranscript("new blind signature")
transcript.AppendMessage([]byte("random commitment"), commitment.ToAffineCompressed())
transcript.AppendMessage([]byte("blind commitment"), bsc.commitment.ToAffineCompressed())
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm := transcript.ExtractBytes([]byte("blind signature context challenge"), 64)
challenge, err := bsc.challenge.SetBytesWide(okm)
if err != nil {
return err
}
if challenge.Cmp(bsc.challenge) != 0 {
return fmt.Errorf("invalid proof")
}
return nil
}
// ToBlindSignature converts a blind signature where
// msgs are known to the signer
// `msgs` is an index to message map where the index
// corresponds to the index in `generators`
func (bsc BlindSignatureContext) ToBlindSignature(
msgs map[int]curves.Scalar,
sk *SecretKey,
generators *MessageGenerators,
nonce common.Nonce,
) (*BlindSignature, error) {
if sk == nil || generators == nil || nonce == nil {
return nil, internal.ErrNilArguments
}
if sk.value.IsZero() {
return nil, fmt.Errorf("invalid secret key")
}
tv1 := make([]int, 0, len(msgs))
for i := range msgs {
if i > generators.length {
return nil, fmt.Errorf("not enough message generators")
}
tv1 = append(tv1, i)
}
sort.Ints(tv1)
signingMsgs := make([]curves.Scalar, len(msgs))
for i, index := range tv1 {
signingMsgs[i] = msgs[index]
}
err := bsc.Verify(tv1, generators, nonce)
if err != nil {
return nil, err
}
drbg := sha3.NewShake256()
_, _ = drbg.Write(sk.value.Bytes())
addDeterministicNonceData(generators, signingMsgs, drbg)
// Should yield non-zero values for `e` and `s`, very small likelihood of being zero
e := getNonZeroScalar(sk.value, drbg)
s := getNonZeroScalar(sk.value, drbg)
exp, err := e.Add(sk.value).Invert()
if err != nil {
return nil, err
}
// B = g1+h_0^s+h_1^m_1...
points := make([]curves.Point, len(msgs)+3)
scalars := make([]curves.Scalar, len(msgs)+3)
points[0] = bsc.commitment
points[1] = bsc.commitment.Generator()
points[2] = generators.Get(0)
scalars[0] = sk.value.One()
scalars[1] = sk.value.One()
scalars[2] = s
i := 3
for idx, m := range msgs {
points[i] = generators.Get(idx + 1)
scalars[i] = m
i++
}
b := bsc.commitment.SumOfProducts(points, scalars)
return &BlindSignature{
a: b.Mul(exp).(curves.PairingPoint),
e: e,
s: s,
}, nil
}
@@ -0,0 +1,98 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
crand "crypto/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestBlindSignatureContext(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
require.NotNil(t, pk)
require.NotNil(t, sk)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
nonce := curve.Scalar.Random(crand.Reader)
msgs := make(map[int]curves.Scalar, 1)
msgs[0] = curve.Scalar.Hash([]byte("identifier"))
ctx, blinding, err := NewBlindSignatureContext(curve, msgs, generators, nonce, crand.Reader)
require.NoError(t, err)
require.NotNil(t, blinding)
require.False(t, blinding.IsZero())
require.NotNil(t, ctx)
require.False(t, ctx.commitment.IsIdentity())
require.False(t, ctx.challenge.IsZero())
for _, p := range ctx.proofs {
require.False(t, p.IsZero())
}
delete(msgs, 0)
msgs[1] = curve.Scalar.Hash([]byte("firstname"))
msgs[2] = curve.Scalar.Hash([]byte("lastname"))
msgs[3] = curve.Scalar.Hash([]byte("age"))
blindSig, err := ctx.ToBlindSignature(msgs, sk, generators, nonce)
require.NoError(t, err)
require.NotNil(t, blindSig)
require.False(t, blindSig.a.IsIdentity())
require.False(t, blindSig.e.IsZero())
require.False(t, blindSig.s.IsZero())
sig := blindSig.ToUnblinded(blinding)
msgs[0] = curve.Scalar.Hash([]byte("identifier"))
var sigMsgs [4]curves.Scalar
for i := 0; i < 4; i++ {
sigMsgs[i] = msgs[i]
}
err = pk.Verify(sig, generators, sigMsgs[:])
require.NoError(t, err)
}
func TestBlindSignatureContextMarshalBinary(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
require.NotNil(t, pk)
require.NotNil(t, sk)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
nonce := curve.Scalar.Random(crand.Reader)
msgs := make(map[int]curves.Scalar, 1)
msgs[0] = curve.Scalar.Hash([]byte("identifier"))
ctx, blinding, err := NewBlindSignatureContext(curve, msgs, generators, nonce, crand.Reader)
require.NoError(t, err)
require.NotNil(t, blinding)
require.False(t, blinding.IsZero())
require.NotNil(t, ctx)
require.False(t, ctx.commitment.IsIdentity())
require.False(t, ctx.challenge.IsZero())
for _, p := range ctx.proofs {
require.False(t, p.IsZero())
}
data, err := ctx.MarshalBinary()
require.NoError(t, err)
require.NotNil(t, data)
ctx2 := new(BlindSignatureContext).Init(curve)
err = ctx2.UnmarshalBinary(data)
require.NoError(t, err)
require.Equal(t, ctx.challenge.Cmp(ctx2.challenge), 0)
require.True(t, ctx.commitment.Equal(ctx2.commitment))
require.Equal(t, len(ctx.proofs), len(ctx2.proofs))
for i, p := range ctx.proofs {
require.Equal(t, p.Cmp(ctx2.proofs[i]), 0)
}
}
+77
View File
@@ -0,0 +1,77 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
"errors"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// MessageGenerators are used to sign a vector of commitments for
// a BBS+ signature. These must be the same generators used by
// sign, verify, prove, and open
//
// These are generated in a deterministic manner. By using
// MessageGenerators in this way, the generators do not need to be
// stored alongside the public key and the same key can be used to sign
// an arbitrary number of messages
// Generators are created by computing
// H_i = H_G1(W || I2OSP(0, 4) || I2OSP(0, 1) || I2OSP(length, 4))
// where I2OSP means Integer to Octet Stream Primitive and
// I2OSP represents an integer in a statically sized byte array.
// `W` is the BBS+ public key.
// Internally we store the 201 byte state since the only value that changes
// is the index
type MessageGenerators struct {
// Blinding factor generator, stored, so we know what points to return in `Get`
h0 curves.PairingPoint
length int
state [201]byte
}
// Init set the message generators to the default state
func (msgg *MessageGenerators) Init(w *PublicKey, length int) (*MessageGenerators, error) {
if length < 0 {
return nil, errors.New("length should be nonnegative")
}
msgg.length = length
for i := range msgg.state {
msgg.state[i] = 0
}
copy(msgg.state[:192], w.value.ToAffineUncompressed())
msgg.state[197] = byte(length >> 24)
msgg.state[198] = byte(length >> 16)
msgg.state[199] = byte(length >> 8)
msgg.state[200] = byte(length)
var ok bool
msgg.h0, ok = w.value.OtherGroup().Hash(msgg.state[:]).(curves.PairingPoint)
if !ok {
return nil, errors.New("incorrect type conversion")
}
return msgg, nil
}
func (msgg MessageGenerators) Get(i int) curves.PairingPoint {
if i <= 0 {
return msgg.h0
}
if i > msgg.length {
return nil
}
state := msgg.state
state[193] = byte(i >> 24)
state[194] = byte(i >> 16)
state[195] = byte(i >> 8)
state[196] = byte(i)
point, ok := msgg.h0.Hash(msgg.state[:]).(curves.PairingPoint)
if !ok {
return nil
}
return point
}
+159
View File
@@ -0,0 +1,159 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
"fmt"
"io"
"github.com/gtank/merlin"
"github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/signatures/common"
)
// PokSignature a.k.a. Proof of Knowledge of a Signature
// is used by the prover to convince a verifier
// that they possess a valid signature and
// can selectively disclose a set of signed messages
type PokSignature struct {
// These values correspond to values with the same name
// as section 4.5 in <https://eprint.iacr.org/2016/663.pdf>
aPrime, aBar, d curves.PairingPoint
// proof1 for proving signature
// proof2 for selective disclosure
proof1, proof2 *common.ProofCommittedBuilder
// secrets1 for proving signature
// secrets2 for proving relation
// g1 * h1^m1 * h2^m2.... for all disclosed messages
// m_i == d^r3 * h_0^{-s_prime} * h1^-m1 * h2^-m2.... for all undisclosed messages m_i
secrets1, secrets2 []curves.Scalar
}
// NewPokSignature creates the initial proof data before a Fiat-Shamir calculation
func NewPokSignature(sig *Signature,
generators *MessageGenerators,
msgs []common.ProofMessage,
reader io.Reader,
) (*PokSignature, error) {
if len(msgs) != generators.length {
return nil, fmt.Errorf("mismatch messages and generators")
}
r1 := getNonZeroScalar(sig.s, reader)
r2 := getNonZeroScalar(sig.s, reader)
r3, err := r1.Invert()
if err != nil {
return nil, err
}
sigMsgs := make([]curves.Scalar, len(msgs))
for i, m := range msgs {
sigMsgs[i] = m.GetMessage()
}
b := computeB(sig.s, sigMsgs, generators)
aPrime, ok := sig.a.Mul(r1).(curves.PairingPoint)
if !ok {
return nil, fmt.Errorf("invalid point")
}
aBar, ok := b.Mul(r1).Sub(aPrime.Mul(sig.e)).(curves.PairingPoint)
if !ok {
return nil, fmt.Errorf("invalid point")
}
// d = b * r1 + h0 * r2
d, ok := aPrime.SumOfProducts([]curves.Point{b, generators.h0}, []curves.Scalar{r1, r2}).(curves.PairingPoint)
if !ok {
return nil, fmt.Errorf("invalid point")
}
// s' = s + r2r3
sPrime := sig.s.Add(r2.Mul(r3))
// For proving relation aBar - d = aPrime * -e + h0 * r2
curve := curves.Curve{
Scalar: sig.s.Zero(),
Point: sig.a.Identity(),
}
proof1 := common.NewProofCommittedBuilder(&curve)
// For aPrime * -e
err = proof1.CommitRandom(aPrime, reader)
if err != nil {
return nil, err
}
err = proof1.CommitRandom(generators.h0, reader)
if err != nil {
return nil, err
}
secrets1 := []curves.Scalar{sig.e, r2}
// For selective disclosure
proof2 := common.NewProofCommittedBuilder(&curve)
// For d * -r3
err = proof2.CommitRandom(d.Neg(), reader)
if err != nil {
return nil, err
}
// For h0 * s'
err = proof2.CommitRandom(generators.h0, reader)
if err != nil {
return nil, err
}
secrets2 := make([]curves.Scalar, 0, len(msgs)+2)
secrets2 = append(secrets2, r3)
secrets2 = append(secrets2, sPrime)
for i, m := range msgs {
if m.IsHidden() {
err = proof2.Commit(generators.Get(i+1), m.GetBlinding(reader))
if err != nil {
return nil, err
}
secrets2 = append(secrets2, m.GetMessage())
}
}
return &PokSignature{
aPrime,
aBar,
d,
proof1,
proof2,
secrets1,
secrets2,
}, nil
}
// GetChallengeContribution returns the bytes that should be added to
// a sigma protocol transcript for generating the challenge
func (pok *PokSignature) GetChallengeContribution(transcript *merlin.Transcript) {
transcript.AppendMessage([]byte("A'"), pok.aPrime.ToAffineCompressed())
transcript.AppendMessage([]byte("Abar"), pok.aBar.ToAffineCompressed())
transcript.AppendMessage([]byte("D"), pok.d.ToAffineCompressed())
transcript.AppendMessage([]byte("Proof1"), pok.proof1.GetChallengeContribution())
transcript.AppendMessage([]byte("Proof2"), pok.proof2.GetChallengeContribution())
}
// GenerateProof converts the blinding factors and secrets into Schnorr proofs
func (pok *PokSignature) GenerateProof(challenge curves.Scalar) (*PokSignatureProof, error) {
proof1, err := pok.proof1.GenerateProof(challenge, pok.secrets1)
if err != nil {
return nil, err
}
proof2, err := pok.proof2.GenerateProof(challenge, pok.secrets2)
if err != nil {
return nil, err
}
return &PokSignatureProof{
aPrime: pok.aPrime,
aBar: pok.aBar,
d: pok.d,
proof1: proof1,
proof2: proof2,
}, nil
}
+214
View File
@@ -0,0 +1,214 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
"errors"
"fmt"
"github.com/gtank/merlin"
"github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/signatures/common"
)
// PokSignatureProof is the actual proof sent from a prover
// to a verifier that contains a proof of knowledge of a signature
// and the selective disclosure proof
type PokSignatureProof struct {
aPrime, aBar, d curves.PairingPoint
proof1, proof2 []curves.Scalar
}
// Init creates an empty proof to a specific curve
// which should be followed by UnmarshalBinary
func (pok *PokSignatureProof) Init(curve *curves.PairingCurve) *PokSignatureProof {
pok.aPrime = curve.Scalar.Point().(curves.PairingPoint).OtherGroup()
pok.aBar = pok.aPrime
pok.d = pok.aPrime
pok.proof1 = []curves.Scalar{
curve.Scalar.Zero(),
curve.Scalar.Zero(),
}
pok.proof2 = make([]curves.Scalar, 0)
return pok
}
func (pok *PokSignatureProof) MarshalBinary() ([]byte, error) {
data := append(pok.aPrime.ToAffineCompressed(), pok.aBar.ToAffineCompressed()...)
data = append(data, pok.d.ToAffineCompressed()...)
for _, p := range pok.proof1 {
data = append(data, p.Bytes()...)
}
for _, p := range pok.proof2 {
data = append(data, p.Bytes()...)
}
return data, nil
}
func (pok *PokSignatureProof) UnmarshalBinary(in []byte) error {
scSize := len(pok.proof1[0].Bytes())
ptSize := len(pok.aPrime.ToAffineCompressed())
minSize := scSize*4 + ptSize*3
inSize := len(in)
if inSize < minSize {
return fmt.Errorf("invalid byte sequence")
}
if (inSize-ptSize)%scSize != 0 {
return fmt.Errorf("invalid byte sequence")
}
secretCnt := ((inSize - ptSize*3) / scSize) - 2
offset := 0
end := ptSize
aPrime, err := pok.aPrime.FromAffineCompressed(in[offset:end])
if err != nil {
return err
}
offset = end
end += ptSize
aBar, err := pok.aBar.FromAffineCompressed(in[offset:end])
if err != nil {
return err
}
offset = end
end += ptSize
d, err := pok.d.FromAffineCompressed(in[offset:end])
if err != nil {
return err
}
offset = end
end += scSize
proof1i0, err := pok.proof1[0].SetBytes(in[offset:end])
if err != nil {
return err
}
offset = end
end += scSize
proof1i1, err := pok.proof1[1].SetBytes(in[offset:end])
if err != nil {
return err
}
proof2 := make([]curves.Scalar, secretCnt)
for i := 0; i < secretCnt; i++ {
offset = end
end += scSize
proof2[i], err = pok.proof1[0].SetBytes(in[offset:end])
if err != nil {
return err
}
}
var ok bool
pok.aPrime, ok = aPrime.(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
pok.aBar, ok = aBar.(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
pok.d, ok = d.(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
pok.proof1[0] = proof1i0
pok.proof1[1] = proof1i1
pok.proof2 = proof2
return nil
}
// GetChallengeContribution converts the committed values to bytes
// for the Fiat-Shamir challenge
func (pok PokSignatureProof) GetChallengeContribution(
generators *MessageGenerators,
revealedMessages map[int]curves.Scalar,
challenge common.Challenge,
transcript *merlin.Transcript,
) {
transcript.AppendMessage([]byte("A'"), pok.aPrime.ToAffineCompressed())
transcript.AppendMessage([]byte("Abar"), pok.aBar.ToAffineCompressed())
transcript.AppendMessage([]byte("D"), pok.d.ToAffineCompressed())
proof1Points := []curves.Point{pok.aBar.Sub(pok.d), pok.aPrime, generators.h0}
proof1Scalars := []curves.Scalar{challenge, pok.proof1[0], pok.proof1[1]}
commitmentProof1 := pok.aPrime.SumOfProducts(proof1Points, proof1Scalars)
transcript.AppendMessage([]byte("Proof1"), commitmentProof1.ToAffineCompressed())
rPoints := make([]curves.Point, 1, len(revealedMessages)+1)
rScalars := make([]curves.Scalar, 1, len(revealedMessages)+1)
rPoints[0] = pok.aPrime.Generator()
rScalars[0] = pok.proof1[0].One()
for idx, msg := range revealedMessages {
rPoints = append(rPoints, generators.Get(idx+1))
rScalars = append(rScalars, msg)
}
r := pok.aPrime.SumOfProducts(rPoints, rScalars)
pts := 3 + generators.length - len(revealedMessages)
proof2Points := make([]curves.Point, 3, pts)
proof2Scalars := make([]curves.Scalar, 3, pts)
// R * c
proof2Points[0] = r
proof2Scalars[0] = challenge
// D * -r3Hat
proof2Points[1] = pok.d.Neg()
proof2Scalars[1] = pok.proof2[0]
// H0 * s'Hat
proof2Points[2] = generators.h0
proof2Scalars[2] = pok.proof2[1]
j := 2
for i := 0; i < generators.length; i++ {
if _, contains := revealedMessages[i]; contains {
continue
}
proof2Points = append(proof2Points, generators.Get(i+1))
proof2Scalars = append(proof2Scalars, pok.proof2[j])
j++
}
commitmentProof2 := r.SumOfProducts(proof2Points, proof2Scalars)
transcript.AppendMessage([]byte("Proof2"), commitmentProof2.ToAffineCompressed())
}
// VerifySigPok only validates the signature proof,
// the selective disclosure proof is checked by
// verifying
// pok.challenge == computedChallenge
func (pok PokSignatureProof) VerifySigPok(pk *PublicKey) bool {
return !pk.value.IsIdentity() &&
!pok.aPrime.IsIdentity() &&
!pok.aBar.IsIdentity() &&
pok.aPrime.MultiPairing(pok.aPrime, pk.value, pok.aBar, pk.value.Generator().Neg().(curves.PairingPoint)).
IsOne()
}
// Verify checks a signature proof of knowledge and selective disclosure proof
func (pok PokSignatureProof) Verify(
revealedMsgs map[int]curves.Scalar,
pk *PublicKey,
generators *MessageGenerators,
nonce common.Nonce,
challenge common.Challenge,
transcript *merlin.Transcript,
) bool {
pok.GetChallengeContribution(generators, revealedMsgs, challenge, transcript)
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm := transcript.ExtractBytes([]byte("signature proof of knowledge"), 64)
vChallenge, err := pok.proof1[0].SetBytesWide(okm)
if err != nil {
return false
}
return pok.VerifySigPok(pk) && challenge.Cmp(vChallenge) == 0
}
+319
View File
@@ -0,0 +1,319 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
crand "crypto/rand"
"testing"
"github.com/gtank/merlin"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/signatures/common"
)
func TestPokSignatureProofSomeMessagesRevealed(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
require.NotNil(t, sk)
require.NotNil(t, pk)
require.False(t, sk.value.IsZero())
require.False(t, pk.value.IsIdentity())
_, ok := pk.value.(*curves.PointBls12381G2)
require.True(t, ok)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
msgs := []curves.Scalar{
curve.Scalar.New(2),
curve.Scalar.New(3),
curve.Scalar.New(4),
curve.Scalar.New(5),
}
sig, err := sk.Sign(generators, msgs)
require.NoError(t, err)
require.NotNil(t, sig)
require.False(t, sig.a.IsIdentity())
require.False(t, sig.e.IsZero())
require.False(t, sig.s.IsZero())
proofMsgs := []common.ProofMessage{
&common.ProofSpecificMessage{
Message: msgs[0],
},
&common.ProofSpecificMessage{
Message: msgs[1],
},
&common.RevealedMessage{
Message: msgs[2],
},
&common.RevealedMessage{
Message: msgs[3],
},
}
pok, err := NewPokSignature(sig, generators, proofMsgs, crand.Reader)
require.NoError(t, err)
require.NotNil(t, pok)
nonce := curve.Scalar.Random(crand.Reader)
transcript := merlin.NewTranscript("TestPokSignatureProofWorks")
pok.GetChallengeContribution(transcript)
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm := transcript.ExtractBytes([]byte("signature proof of knowledge"), 64)
challenge, err := curve.Scalar.SetBytesWide(okm)
require.NoError(t, err)
pokSig, err := pok.GenerateProof(challenge)
require.NoError(t, err)
require.NotNil(t, pokSig)
require.True(t, pokSig.VerifySigPok(pk))
revealedMsgs := map[int]curves.Scalar{
2: msgs[2],
3: msgs[3],
}
// Manual verify to show how when used in conjunction with other ZKPs
transcript = merlin.NewTranscript("TestPokSignatureProofWorks")
pokSig.GetChallengeContribution(generators, revealedMsgs, challenge, transcript)
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm = transcript.ExtractBytes([]byte("signature proof of knowledge"), 64)
vChallenge, err := curve.Scalar.SetBytesWide(okm)
require.NoError(t, err)
require.Equal(t, challenge.Cmp(vChallenge), 0)
// Use the all-inclusive method
transcript = merlin.NewTranscript("TestPokSignatureProofWorks")
require.True(t, pokSig.Verify(revealedMsgs, pk, generators, nonce, challenge, transcript))
}
func TestPokSignatureProofAllMessagesRevealed(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
require.NotNil(t, sk)
require.NotNil(t, pk)
require.False(t, sk.value.IsZero())
require.False(t, pk.value.IsIdentity())
_, ok := pk.value.(*curves.PointBls12381G2)
require.True(t, ok)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
msgs := []curves.Scalar{
curve.Scalar.New(2),
curve.Scalar.New(3),
curve.Scalar.New(4),
curve.Scalar.New(5),
}
sig, err := sk.Sign(generators, msgs)
require.NoError(t, err)
require.NotNil(t, sig)
require.False(t, sig.a.IsIdentity())
require.False(t, sig.e.IsZero())
require.False(t, sig.s.IsZero())
proofMsgs := []common.ProofMessage{
&common.RevealedMessage{
Message: msgs[0],
},
&common.RevealedMessage{
Message: msgs[1],
},
&common.RevealedMessage{
Message: msgs[2],
},
&common.RevealedMessage{
Message: msgs[3],
},
}
pok, err := NewPokSignature(sig, generators, proofMsgs, crand.Reader)
require.NoError(t, err)
require.NotNil(t, pok)
nonce := curve.Scalar.Random(crand.Reader)
transcript := merlin.NewTranscript("TestPokSignatureProofWorks")
pok.GetChallengeContribution(transcript)
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm := transcript.ExtractBytes([]byte("signature proof of knowledge"), 64)
challenge, err := curve.Scalar.SetBytesWide(okm)
require.NoError(t, err)
pokSig, err := pok.GenerateProof(challenge)
require.NoError(t, err)
require.NotNil(t, pokSig)
require.True(t, pokSig.VerifySigPok(pk))
revealedMsgs := map[int]curves.Scalar{
0: msgs[0],
1: msgs[1],
2: msgs[2],
3: msgs[3],
}
// Manual verify to show how when used in conjunction with other ZKPs
transcript = merlin.NewTranscript("TestPokSignatureProofWorks")
pokSig.GetChallengeContribution(generators, revealedMsgs, challenge, transcript)
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm = transcript.ExtractBytes([]byte("signature proof of knowledge"), 64)
vChallenge, err := curve.Scalar.SetBytesWide(okm)
require.NoError(t, err)
require.Equal(t, challenge.Cmp(vChallenge), 0)
// Use the all-inclusive method
transcript = merlin.NewTranscript("TestPokSignatureProofWorks")
require.True(t, pokSig.Verify(revealedMsgs, pk, generators, nonce, challenge, transcript))
}
func TestPokSignatureProofAllMessagesHidden(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
require.NotNil(t, sk)
require.NotNil(t, pk)
require.False(t, sk.value.IsZero())
require.False(t, pk.value.IsIdentity())
_, ok := pk.value.(*curves.PointBls12381G2)
require.True(t, ok)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
msgs := []curves.Scalar{
curve.Scalar.New(2),
curve.Scalar.New(3),
curve.Scalar.New(4),
curve.Scalar.New(5),
}
sig, err := sk.Sign(generators, msgs)
require.NoError(t, err)
require.NotNil(t, sig)
require.False(t, sig.a.IsIdentity())
require.False(t, sig.e.IsZero())
require.False(t, sig.s.IsZero())
proofMsgs := []common.ProofMessage{
&common.ProofSpecificMessage{
Message: msgs[0],
},
&common.ProofSpecificMessage{
Message: msgs[1],
},
&common.ProofSpecificMessage{
Message: msgs[2],
},
&common.ProofSpecificMessage{
Message: msgs[3],
},
}
pok, err := NewPokSignature(sig, generators, proofMsgs, crand.Reader)
require.NoError(t, err)
require.NotNil(t, pok)
nonce := curve.Scalar.Random(crand.Reader)
transcript := merlin.NewTranscript("TestPokSignatureProofWorks")
pok.GetChallengeContribution(transcript)
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm := transcript.ExtractBytes([]byte("signature proof of knowledge"), 64)
challenge, err := curve.Scalar.SetBytesWide(okm)
require.NoError(t, err)
pokSig, err := pok.GenerateProof(challenge)
require.NoError(t, err)
require.NotNil(t, pokSig)
require.True(t, pokSig.VerifySigPok(pk))
revealedMsgs := map[int]curves.Scalar{}
// Manual verify to show how when used in conjunction with other ZKPs
transcript = merlin.NewTranscript("TestPokSignatureProofWorks")
pokSig.GetChallengeContribution(generators, revealedMsgs, challenge, transcript)
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
okm = transcript.ExtractBytes([]byte("signature proof of knowledge"), 64)
vChallenge, err := curve.Scalar.SetBytesWide(okm)
require.NoError(t, err)
require.Equal(t, challenge.Cmp(vChallenge), 0)
// Use the all-inclusive method
transcript = merlin.NewTranscript("TestPokSignatureProofWorks")
require.True(t, pokSig.Verify(revealedMsgs, pk, generators, nonce, challenge, transcript))
}
func TestPokSignatureProofMarshalBinary(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
require.NotNil(t, sk)
require.NotNil(t, pk)
require.False(t, sk.value.IsZero())
require.False(t, pk.value.IsIdentity())
_, ok := pk.value.(*curves.PointBls12381G2)
require.True(t, ok)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
msgs := []curves.Scalar{
curve.Scalar.New(2),
curve.Scalar.New(3),
curve.Scalar.New(4),
curve.Scalar.New(5),
}
sig, err := sk.Sign(generators, msgs)
require.NoError(t, err)
require.NotNil(t, sig)
require.False(t, sig.a.IsIdentity())
require.False(t, sig.e.IsZero())
require.False(t, sig.s.IsZero())
proofMsgs := []common.ProofMessage{
&common.ProofSpecificMessage{
Message: msgs[0],
},
&common.ProofSpecificMessage{
Message: msgs[1],
},
&common.RevealedMessage{
Message: msgs[2],
},
&common.RevealedMessage{
Message: msgs[3],
},
}
pok, err := NewPokSignature(sig, generators, proofMsgs, crand.Reader)
require.NoError(t, err)
require.NotNil(t, pok)
nonce := curve.Scalar.Random(crand.Reader)
transcript := merlin.NewTranscript("TestPokSignatureProofMarshalBinary")
pok.GetChallengeContribution(transcript)
transcript.AppendMessage([]byte("nonce"), nonce.Bytes())
challenge, err := curve.Scalar.SetBytesWide(
transcript.ExtractBytes([]byte("signature proof of knowledge"), 64),
)
require.NoError(t, err)
pokSig, err := pok.GenerateProof(challenge)
require.NoError(t, err)
require.NotNil(t, pokSig)
data, err := pokSig.MarshalBinary()
require.NoError(t, err)
require.NotNil(t, data)
pokSig2 := new(PokSignatureProof).Init(curve)
err = pokSig2.UnmarshalBinary(data)
require.NoError(t, err)
require.True(t, pokSig.aPrime.Equal(pokSig2.aPrime))
require.True(t, pokSig.aBar.Equal(pokSig2.aBar))
require.True(t, pokSig.d.Equal(pokSig2.d))
require.Equal(t, len(pokSig.proof1), len(pokSig2.proof1))
require.Equal(t, len(pokSig.proof2), len(pokSig2.proof2))
for i, p := range pokSig.proof1 {
require.Equal(t, p.Cmp(pokSig2.proof1[i]), 0)
}
for i, p := range pokSig.proof2 {
require.Equal(t, p.Cmp(pokSig2.proof2[i]), 0)
}
}
+77
View File
@@ -0,0 +1,77 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
"errors"
"fmt"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// PublicKey is a BBS+ verification key
type PublicKey struct {
value curves.PairingPoint
}
func (pk *PublicKey) Init(curve *curves.PairingCurve) *PublicKey {
pk.value = curve.NewG2IdentityPoint()
return pk
}
func (pk PublicKey) MarshalBinary() ([]byte, error) {
return pk.value.ToAffineCompressed(), nil
}
func (pk *PublicKey) UnmarshalBinary(in []byte) error {
value, err := pk.value.FromAffineCompressed(in)
if err != nil {
return err
}
var ok bool
pk.value, ok = value.(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
return nil
}
// Verify checks a signature where all messages are known to the verifier
func (pk PublicKey) Verify(
signature *Signature,
generators *MessageGenerators,
msgs []curves.Scalar,
) error {
if generators.length < len(msgs) {
return fmt.Errorf("not enough message generators")
}
if len(msgs) < 1 {
return fmt.Errorf("invalid messages")
}
// Identity Point will always return true which is not what we want
if pk.value.IsIdentity() {
return fmt.Errorf("invalid public key")
}
if signature.a.IsIdentity() {
return fmt.Errorf("invalid signature")
}
a, ok := pk.value.Generator().Mul(signature.e).Add(pk.value).(curves.PairingPoint)
if !ok {
return fmt.Errorf("not a valid point")
}
b, ok := computeB(signature.s, msgs, generators).Neg().(curves.PairingPoint)
if !ok {
return fmt.Errorf("not a valid point")
}
res := a.MultiPairing(signature.a, a, b, pk.value.Generator().(curves.PairingPoint))
if !res.IsOne() {
return fmt.Errorf("invalid result")
}
return nil
}
+185
View File
@@ -0,0 +1,185 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
crand "crypto/rand"
"crypto/sha256"
"errors"
"fmt"
"io"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// SecretKey is a BBS+ signing key
type SecretKey struct {
value curves.PairingScalar
}
func NewSecretKey(curve *curves.PairingCurve) (*SecretKey, error) {
// The salt used with generating secret keys
// See section 2.3 from https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04
const hkdfKeyGenSalt = "BLS-SIG-KEYGEN-SALT-"
const Size = 33
var ikm [Size]byte
cnt, err := crand.Read(ikm[:32])
if err != nil {
return nil, err
}
if cnt != Size-1 {
return nil, fmt.Errorf("unable to read sufficient random data")
}
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04#section-2.3
h := sha256.New()
n, err := h.Write([]byte(hkdfKeyGenSalt))
if err != nil {
return nil, err
}
if n != len(hkdfKeyGenSalt) {
return nil, fmt.Errorf("incorrect salt bytes written to be hashed")
}
salt := h.Sum(nil)
// Leaves key_info parameter as the default empty string
// and just adds parameter I2OSP(L, 2)
kdf := hkdf.New(sha256.New, ikm[:], salt, []byte{0, 48})
var okm [64]byte
read, err := kdf.Read(okm[:48])
if err != nil {
return nil, err
}
if read != 48 {
return nil, fmt.Errorf("failed to create secret key")
}
v, err := curve.Scalar.SetBytesWide(okm[:])
if err != nil {
return nil, err
}
value, ok := v.(curves.PairingScalar)
if !ok {
return nil, fmt.Errorf("invalid scalar")
}
return &SecretKey{
value: value.SetPoint(curve.PointG2),
}, nil
}
func NewKeys(curve *curves.PairingCurve) (*PublicKey, *SecretKey, error) {
sk, err := NewSecretKey(curve)
if err != nil {
return nil, nil, err
}
return sk.PublicKey(), sk, nil
}
func (sk *SecretKey) Init(curve *curves.PairingCurve) *SecretKey {
sk.value = curve.NewScalar()
return sk
}
func (sk SecretKey) MarshalBinary() ([]byte, error) {
return sk.value.Bytes(), nil
}
func (sk *SecretKey) UnmarshalBinary(in []byte) error {
value, err := sk.value.SetBytes(in)
if err != nil {
return err
}
var ok bool
sk.value, ok = value.(curves.PairingScalar)
if !ok {
return errors.New("incorrect type conversion")
}
return nil
}
// Sign generates a new signature where all messages are known to the signer
func (sk *SecretKey) Sign(generators *MessageGenerators, msgs []curves.Scalar) (*Signature, error) {
if generators.length < len(msgs) {
return nil, fmt.Errorf("not enough message generators")
}
if len(msgs) < 1 {
return nil, fmt.Errorf("invalid messages")
}
if sk.value.IsZero() {
return nil, fmt.Errorf("invalid secret key")
}
drbg := sha3.NewShake256()
_, _ = drbg.Write(sk.value.Bytes())
addDeterministicNonceData(generators, msgs, drbg)
// Should yield non-zero values for `e` and `s`, very small likelihood of being zero
e := getNonZeroScalar(sk.value, drbg)
s := getNonZeroScalar(sk.value, drbg)
b := computeB(s, msgs, generators)
exp, err := e.Add(sk.value).Invert()
if err != nil {
return nil, err
}
return &Signature{
a: b.Mul(exp).(curves.PairingPoint),
e: e,
s: s,
}, nil
}
// PublicKey returns the corresponding public key
func (sk *SecretKey) PublicKey() *PublicKey {
return &PublicKey{
value: sk.value.Point().Generator().Mul(sk.value).(curves.PairingPoint),
}
}
// computes g1 + s * h0 + msgs[0] * h[0] + msgs[1] * h[1] ...
func computeB(
s curves.Scalar,
msgs []curves.Scalar,
generators *MessageGenerators,
) curves.PairingPoint {
nMsgs := len(msgs)
points := make([]curves.Point, nMsgs+2)
points[1] = generators.Get(0)
points[0] = points[1].Generator()
scalars := make([]curves.Scalar, nMsgs+2)
scalars[0] = msgs[0].One()
scalars[1] = s
for i, m := range msgs {
points[i+2] = generators.Get(i + 1)
scalars[i+2] = m
}
pt := points[0].SumOfProducts(points, scalars)
return pt.(curves.PairingPoint)
}
func addDeterministicNonceData(
generators *MessageGenerators,
msgs []curves.Scalar,
drbg io.Writer,
) {
for i := 0; i <= generators.length; i++ {
_, _ = drbg.Write(generators.Get(i).ToAffineUncompressed())
}
for _, m := range msgs {
_, _ = drbg.Write(m.Bytes())
}
}
func getNonZeroScalar(sc curves.Scalar, reader io.Reader) curves.Scalar {
// Should yield non-zero values for `e` and `s`, very small likelihood of being zero
e := sc.Random(reader)
for e.IsZero() {
e = sc.Random(reader)
}
return e
}
+67
View File
@@ -0,0 +1,67 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package bbs is an implementation of BBS+ signature of https://eprint.iacr.org/2016/663.pdf
package bbs
import (
"errors"
"fmt"
"github.com/sonr-io/sonr/crypto/core/curves"
)
// Signature is a BBS+ signature
// as described in 4.3 in
// <https://eprint.iacr.org/2016/663.pdf>
type Signature struct {
a curves.PairingPoint
e, s curves.Scalar
}
// Init creates an empty signature to a specific curve
// which should be followed by UnmarshalBinary or Create
func (sig *Signature) Init(curve *curves.PairingCurve) *Signature {
sig.a = curve.NewG1IdentityPoint()
sig.e = curve.NewScalar()
sig.s = curve.NewScalar()
return sig
}
func (sig Signature) MarshalBinary() ([]byte, error) {
out := append(sig.a.ToAffineCompressed(), sig.e.Bytes()...)
out = append(out, sig.s.Bytes()...)
return out, nil
}
func (sig *Signature) UnmarshalBinary(data []byte) error {
pointLength := len(sig.a.ToAffineCompressed())
scalarLength := len(sig.s.Bytes())
expectedLength := pointLength + scalarLength*2
if len(data) != expectedLength {
return fmt.Errorf("invalid byte sequence")
}
a, err := sig.a.FromAffineCompressed(data[:pointLength])
if err != nil {
return err
}
e, err := sig.e.SetBytes(data[pointLength:(pointLength + scalarLength)])
if err != nil {
return err
}
s, err := sig.s.SetBytes(data[(pointLength + scalarLength):])
if err != nil {
return err
}
var ok bool
sig.a, ok = a.(curves.PairingPoint)
if !ok {
return errors.New("incorrect type conversion")
}
sig.e = e
sig.s = s
return nil
}
+81
View File
@@ -0,0 +1,81 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bbs
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
)
func TestSignatureWorks(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
msgs := []curves.Scalar{
curve.Scalar.New(3),
curve.Scalar.New(4),
curve.Scalar.New(5),
curve.Scalar.New(6),
}
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
sig, err := sk.Sign(generators, msgs)
require.NoError(t, err)
err = pk.Verify(sig, generators, msgs)
require.NoError(t, err)
}
func TestSignatureIncorrectMessages(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
msgs := []curves.Scalar{
curve.Scalar.New(3),
curve.Scalar.New(4),
curve.Scalar.New(5),
curve.Scalar.New(6),
}
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
sig, err := sk.Sign(generators, msgs)
require.NoError(t, err)
msgs[0] = curve.Scalar.New(7)
err = pk.Verify(sig, generators, msgs)
require.Error(t, err)
}
func TestSignatureMarshalBinary(t *testing.T) {
curve := curves.BLS12381(&curves.PointBls12381G2{})
msgs := []curves.Scalar{
curve.Scalar.New(3),
curve.Scalar.New(4),
curve.Scalar.New(5),
curve.Scalar.New(6),
}
pk, sk, err := NewKeys(curve)
require.NoError(t, err)
generators, err := new(MessageGenerators).Init(pk, 4)
require.NoError(t, err)
sig, err := sk.Sign(generators, msgs)
require.NoError(t, err)
data, err := sig.MarshalBinary()
require.NoError(t, err)
require.Equal(t, 112, len(data))
sig2 := new(Signature).Init(curve)
err = sig2.UnmarshalBinary(data)
require.NoError(t, err)
require.True(t, sig.a.Equal(sig2.a))
require.Equal(t, sig.e.Cmp(sig2.e), 0)
require.Equal(t, sig.s.Cmp(sig2.s), 0)
}