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
+114
View File
@@ -0,0 +1,114 @@
---
aliases: [README]
tags: []
title: README
linter-yaml-title-alias: README
date created: Wednesday, April 17th 2024, 4:11:40 pm
date modified: Thursday, April 18th 2024, 8:19:25 am
---
## BLS Signatures
An implementation of the Boneh-Lynn-Shacham (BLS) signatures according to the [standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/?include_text=1)
on top of the Barreto-Lynn-Scott (BLS) 12-381 curve. The [BLS12-381 curve](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381#serialization) provides roughly
128-bits of security and BLS is a digital signature with aggregation properties.
We have implemented all three signature schemes described in the [standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/?include_text=1)
which are designed to handle rogue-key attacks differently. These three are:
- **Basic** handles rogue key attacks by requiring all signed messages in an aggregate signature be distinct
- **Message Augmentation** handles rogue key attacks by prepending the public key to the message during signing which ensures that all messages are distinct for different public keys.
- **Proof of Possession** handles rogue key attacks by validating public keys in a separate step called a proof of possession. This allows for faster aggregate verification.
Pairing-friendly curves have two generator-groups &#x1D53E;<sub>1</sub>, &#x1D53E;<sub>2</sub>.
Data in &#x1D53E;<sub>2</sub> is twice the size of &#x1D53E;<sub>1</sub> and operations are slower.
BLS signatures require signatures and public keys to be in opposite groups i.e. signatures in &#x1D53E;<sub>1</sub> and public keys in &#x1D53E;<sub>2</sub> or
signatures in &#x1D53E;<sub>2</sub> and public keys in &#x1D53E;<sub>1</sub>. This means one of two things:
- **Short public keys, long signatures**: Signatures are longer and slower to create, verify, and aggregate but public keys are small and fast to aggregate. Used when signing and verification operations not computed as often or for minimizing storage or bandwidth for public keys.
- **Short signatures, long public keys**: Signatures are short and fast to create, verify, and aggregate but public keys are bigger and slower to aggregate. Used when signing and verification operations are computed often or for minimizing storage or bandwidth for signatures.
This library supports both of these variants for all three signature schemes. The more widely deployed
variant is short public keys, long signatures. We refer to this variant as `UsualBls`. The other variant,
short signatures, long public keys, is named `TinyBls`.
The naming convention follows Sig`SchemeType`[Vt] where **Vt** is short for variant. For example,
- **Usual Bls Basic -> SigBasic**: Provides all the functions for the Basic signature scheme with signatures in &#x1D53E;<sub>2</sub> and public keys in &#x1D53E;<sub>1</sub>
- **Tiny Bls Basic -> SigBasicVt**: Provides all the functions for the Basic signature scheme with signatures in &#x1D53E;<sub>1</sub> and public keys in &#x1D53E;<sub>2</sub>
One final note, in cryptography, it is considered good practice to use domain separation values to limit attacks to specific contexts. The [standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/?include_text=1)
recommends specific values for each scheme but this library also supports supplying custom domain separation values. For example, there are two functions for creating
a BLS signing instance:
- **NewSigBasic()** creates a Basic BLS signature using the recommended domain separation value.
- **NewSigBasicWithDst(dst)** creates a Basic BLS signature using the parameter `dst` as the domain separation value such as in the [Eth2.0 Spec](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/validator.md#attestation-aggregation)
Also implemented is Threshold BLS as described in section 3.2 of [B03](https://www.cc.gatech.edu/~aboldyre/papers/bold.pdf).
- ThresholdKeygen(parts, threshold int) -> ([]\*SecretKeyShare, error)
- PartialSign(share *SecretKeyShare, msg []byte) -> *PartialSignature
- CombineSigs(*PartialSignature…) -> *Signature
### Security Considerations
#### Validating Secret Keys
Low entropy secret keys are a problem since this means an attacker can more easily guess the value and now can impersonate the key holder.
Secret keys are derived using trusted random number generators (TRNG). If the TRNG produces bad entropy
the secret key will be weak. Secret keys are checked to be non-zero values. A zero value secret key
not only fails the entropy test but also yields a public key that will validate any signature.
#### Validating Public Keys
Public keys are ensured to be valid. A valid public key means
it represents a valid, non-identity elliptic curve point in the correct subgroup.
Public keys that are the identity element mean any signature would pass a call to verify.
#### Validating Signatures
Generated signatures are ensured to be valid. A valid signature means
it represents a valid, non-identity elliptic curve point in the correct subgroup.
Signatures that are the identity element mean any signature would pass a call to verify.
Verify checks signatures for non-identity elements in the correct subgroup before checking
for a valid signature.
#### Mitigating Rogue Key Attacks
A rogue key attacks can only happen to multisignature or aggregated signature.
A rogue key attack is where at least one party produces a valid but malicious public key such that the multisignature requires less than the threshold to verify a signature.
There are two ways to mitigate rogue key attacks: Guarantee all signed messages are unique or validate each public key before use. This library offers both solutions.
### Comparison to other BLS Implementations and Integrations
This library has been tested to be compatible with the following implementations
by randomly generating millions of keys and signatures and importing and verifying
between the two libraries.
1. Ethereum's [py_ecc](https://github.com/ethereum/py_ecc)
2. PhoreProject [Go BLS](https://github.com/phoreproject/bls)
3. Herumi's [BLS](https://github.com/herumi/bls-eth-go-binary)
4. Algorand's [Rust BLS Sig](https://crates.io/crates/bls_sigs_ref)
5. Miracl's [Rust and Go BLS sig](https://github.com/miracl/core)
The most common and high risk failures that can occur with library implementations
are low entropy secret keys, malformed public keys, and invalid signature generation.
Low entropy secret keys are a problem since this means an attacker can more easily guess
the value and now can impersonate the key holder.
Malformed public keys and signatures result in invalid addresses and no ability to withdraw funds.
To check for these problems, We tested millions of keys/signatures (vs say thousands or hundreds) to prove that
1. The odds of producing an invalid key/signature is already theoretically low 2<sup>-255</sup>
2. The results of running millions of checks shows that nothing bad happened in 10M attempts
In other words, keys and signatures generated from these libraries can be consumed by this library
and visa-versa.
Some of the libraries implementations for hash to curve were either out of date
or not compliant with the [IETF Spec](https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/).
This meant that signatures generated by this library would not validate with others and visa-versa.
However, public keys were found to be compatible in all libraries.
This library is compliant with the latest version (10 as of this writing).
+208
View File
@@ -0,0 +1,208 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package bls_sig is an implementation of the BLS signature defined in https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
package bls_sig
import (
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"fmt"
"golang.org/x/crypto/hkdf"
"github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
"github.com/sonr-io/sonr/crypto/internal"
"github.com/sonr-io/sonr/crypto/sharing"
)
// Secret key in Fr
const SecretKeySize = 32
// Secret key share with identifier byte in Fr
const SecretKeyShareSize = 33
// The salt used with generating secret keys
// See section 2.3 from https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
const hkdfKeyGenSalt = "BLS-SIG-KEYGEN-SALT-"
// Represents a value mod r where r is the curve order or
// order of the subgroups in G1 and G2
type SecretKey struct {
value *native.Field
}
func allRowsUnique(data [][]byte) bool {
seen := make(map[string]bool)
for _, row := range data {
m := string(row)
if _, ok := seen[m]; ok {
return false
}
seen[m] = true
}
return true
}
func generateRandBytes(count int) ([]byte, error) {
ikm := make([]byte, count)
cnt, err := rand.Read(ikm)
if err != nil {
return nil, err
}
if cnt != count {
return nil, fmt.Errorf("unable to read sufficient random data")
}
return ikm, nil
}
// Creates a new BLS secret key
// Input key material (ikm) MUST be at least 32 bytes long,
// but it MAY be longer.
func (sk SecretKey) Generate(ikm []byte) (*SecretKey, error) {
if len(ikm) < 32 {
return nil, fmt.Errorf("ikm is too short. Must be at least 32")
}
// 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)
ikm = append(ikm, 0)
// Leaves key_info parameter as the default empty string
// and just adds parameter I2OSP(L, 2)
var okm [native.WideFieldBytes]byte
kdf := hkdf.New(sha256.New, ikm, salt, []byte{0, 48})
read, err := kdf.Read(okm[:48])
copy(okm[:48], internal.ReverseScalarBytes(okm[:48]))
if err != nil {
return nil, err
}
if read != 48 {
return nil, fmt.Errorf("failed to create private key")
}
v := bls12381.Bls12381FqNew().SetBytesWide(&okm)
return &SecretKey{value: v}, nil
}
// Serialize a secret key to raw bytes
func (sk SecretKey) MarshalBinary() ([]byte, error) {
bytes := sk.value.Bytes()
return internal.ReverseScalarBytes(bytes[:]), nil
}
// Deserialize a secret key from raw bytes
// Cannot be zero. Must be 32 bytes and cannot be all zeroes.
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03#section-2.3
func (sk *SecretKey) UnmarshalBinary(data []byte) error {
if len(data) != SecretKeySize {
return fmt.Errorf("secret key must be %d bytes", SecretKeySize)
}
zeros := make([]byte, len(data))
if subtle.ConstantTimeCompare(data, zeros) == 1 {
return fmt.Errorf("secret key cannot be zero")
}
var bb [native.FieldBytes]byte
copy(bb[:], internal.ReverseScalarBytes(data))
value, err := bls12381.Bls12381FqNew().SetBytes(&bb)
if err != nil {
return err
}
sk.value = value
return nil
}
// SecretKeyShare is shamir share of a private key
type SecretKeyShare struct {
identifier byte
value []byte
}
// Serialize a secret key share to raw bytes
func (sks SecretKeyShare) MarshalBinary() ([]byte, error) {
var blob [SecretKeyShareSize]byte
l := len(sks.value)
copy(blob[:l], sks.value)
blob[l] = sks.identifier
return blob[:], nil
}
// Deserialize a secret key share from raw bytes
func (sks *SecretKeyShare) UnmarshalBinary(data []byte) error {
if len(data) != SecretKeyShareSize {
return fmt.Errorf("secret key share must be %d bytes", SecretKeyShareSize)
}
zeros := make([]byte, len(data))
if subtle.ConstantTimeCompare(data, zeros) == 1 {
return fmt.Errorf("secret key share cannot be zero")
}
l := len(data)
sks.identifier = data[l-1]
sks.value = make([]byte, SecretKeySize)
copy(sks.value, data[:l])
return nil
}
// thresholdizeSecretKey splits a composite secret key such that
// `threshold` partial signatures can be combined to form a composite signature
func thresholdizeSecretKey(secretKey *SecretKey, threshold, total uint) ([]*SecretKeyShare, error) {
// Verify our parameters are acceptable.
if secretKey == nil {
return nil, fmt.Errorf("secret key is nil")
}
if threshold > total {
return nil, fmt.Errorf("threshold cannot be greater than the total")
}
if threshold == 0 {
return nil, fmt.Errorf("threshold cannot be zero")
}
if total <= 1 {
return nil, fmt.Errorf("total must be larger than 1")
}
if total > 255 || threshold > 255 {
return nil, fmt.Errorf("cannot have more than 255 shares")
}
curve := curves.BLS12381G1()
sss, err := sharing.NewShamir(uint32(threshold), uint32(total), curve)
if err != nil {
return nil, err
}
secret, ok := curve.NewScalar().(*curves.ScalarBls12381)
if !ok {
return nil, fmt.Errorf("invalid curve")
}
secret.Value = secretKey.value
shares, err := sss.Split(secret, rand.Reader)
if err != nil {
return nil, err
}
// Verify we got the expected number of shares
if uint(len(shares)) != total {
return nil, fmt.Errorf("%v != %v shares", len(shares), total)
}
// Package our shares
secrets := make([]*SecretKeyShare, len(shares))
for i, s := range shares {
// users expect BigEndian
sks := &SecretKeyShare{identifier: byte(s.Id), value: s.Value}
secrets[i] = sks
}
return secrets, nil
}
+481
View File
@@ -0,0 +1,481 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"bytes"
"crypto/rand"
"encoding"
"math/big"
"testing"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
"github.com/sonr-io/sonr/crypto/internal"
)
func genSecretKey(t *testing.T) *SecretKey {
ikm := make([]byte, 32)
sk, err := new(SecretKey).Generate(ikm)
if err != nil {
t.Errorf("Couldn't generate secret key")
}
return sk
}
func genRandSecretKey(ikm []byte, t *testing.T) *SecretKey {
sk, err := new(SecretKey).Generate(ikm)
if err != nil {
t.Errorf("Couldn't generate secret key")
}
return sk
}
func genPublicKeyVt(sk *SecretKey, t *testing.T) *PublicKeyVt {
pk, err := sk.GetPublicKeyVt()
if err != nil {
t.Errorf("Expected GetPublicKeyVt to pass but failed: %v", err)
}
return pk
}
func genPublicKey(sk *SecretKey, t *testing.T) *PublicKey {
pk, err := sk.GetPublicKey()
if err != nil {
t.Errorf("GetPublicKey failed. Couldn't generate public key: %v", err)
}
return pk
}
func genSignature(sk *SecretKey, message []byte, t *testing.T) *Signature {
bls := NewSigPop()
sig, err := bls.Sign(sk, message)
if err != nil {
t.Errorf("createSignature couldn't sign message: %v", err)
}
return sig
}
func genSignatureVt(sk *SecretKey, message []byte, t *testing.T) *SignatureVt {
bls := NewSigPopVt()
sig, err := bls.Sign(sk, message)
if err != nil {
t.Errorf("createSignatureVt couldn't sign message: %v", err)
}
return sig
}
func readRand(ikm []byte, t *testing.T) {
n, err := rand.Read(ikm)
if err != nil || n < len(ikm) {
t.Errorf("Not enough data was read or an error occurred")
}
}
func assertSecretKeyGen(seed, expected []byte, t *testing.T) {
sk, err := new(SecretKey).Generate(seed)
if err != nil {
t.Errorf("Expected Generate to succeed but failed")
}
actual, _ := sk.MarshalBinary()
if len(actual) != len(expected) {
t.Errorf("Length of Generate output is incorrect. Expected 32, found: %v\n", len(actual))
}
if !bytes.Equal(actual[:], expected) {
t.Errorf("SecretKey was not as expected")
}
}
func marshalStruct(value encoding.BinaryMarshaler, t *testing.T) []byte {
out, err := value.MarshalBinary()
if err != nil {
t.Errorf("MarshalBinary failed: %v", err)
}
return out
}
func TestSecretKeyZeroBytes(t *testing.T) {
seed := []byte{}
_, err := new(SecretKey).Generate(seed)
if err == nil {
t.Errorf("Expected Generate to fail but succeeded")
}
}
func TestMarshalLeadingZeroes(t *testing.T) {
tests := []struct {
name string
in []byte
}{
{
"no leading zeroes",
[]byte{
74,
53,
59,
227,
218,
192,
145,
160,
167,
230,
64,
98,
3,
114,
245,
225,
226,
228,
64,
23,
23,
193,
231,
156,
172,
111,
251,
168,
246,
144,
86,
4,
},
},
{
"one leading zero byte",
[]byte{
0o0,
53,
59,
227,
218,
192,
145,
160,
167,
230,
64,
98,
3,
114,
245,
225,
226,
228,
64,
23,
23,
193,
231,
156,
172,
111,
251,
168,
246,
144,
86,
4,
},
},
{
"two leading zeroes",
[]byte{
0o0,
0o0,
59,
227,
218,
192,
145,
160,
167,
230,
64,
98,
3,
114,
245,
225,
226,
228,
64,
23,
23,
193,
231,
156,
172,
111,
251,
168,
246,
144,
86,
4,
},
},
}
// Run all the tests!
ss := bls12381.Bls12381FqNew()
for _, test := range tests {
// Marshal
var k big.Int
k.SetBytes(test.in)
ss.SetBigInt(&k)
bytes, err := SecretKey{ss}.MarshalBinary()
if err != nil {
t.Errorf("%v", err)
continue
}
// Test that marshal produces a values of the exected len
t.Run(test.name, func(t *testing.T) {
if len(bytes) != SecretKeySize {
t.Errorf("expected len=%v got len=%v", SecretKeySize, len(bytes))
}
})
// Test that we can also unmarhsal correctly
t.Run(test.name, func(t *testing.T) {
var actual SecretKey
err := actual.UnmarshalBinary(bytes)
// Test for error
if err != nil {
t.Errorf("%v", err)
return
}
// Test for correctness
if actual.value.Cmp(ss) != 0 {
t.Errorf("unmarshaled doens't match original value")
}
})
}
}
func TestSecretKey32Bytes(t *testing.T) {
seed := make([]byte, 32)
expected := []byte{
77,
18,
154,
25,
223,
134,
160,
245,
52,
91,
173,
76,
198,
242,
73,
236,
42,
129,
156,
204,
51,
134,
137,
91,
235,
79,
125,
152,
179,
219,
98,
53,
}
assertSecretKeyGen(seed, expected, t)
}
func TestSecretKey128Bytes(t *testing.T) {
seed := make([]byte, 128)
expected := []byte{
97,
207,
109,
96,
94,
90,
233,
215,
221,
207,
240,
139,
24,
209,
152,
170,
73,
209,
151,
241,
148,
176,
173,
92,
101,
48,
39,
175,
201,
219,
146,
168,
}
assertSecretKeyGen(seed, expected, t)
}
func TestRandomSecretKey(t *testing.T) {
seed := make([]byte, 48)
_, _ = rand.Read(seed)
_, err := new(SecretKey).Generate(seed)
if err != nil {
t.Errorf("Expected Generate to succeed but failed")
}
}
func TestSecretKeyToBytes(t *testing.T) {
sk := genSecretKey(t)
skBytes := marshalStruct(sk, t)
sk1 := new(SecretKey)
err := sk1.UnmarshalBinary(skBytes)
if err != nil {
t.Errorf("Expected UnmarshalBinary to pass but failed: %v", err)
}
out := sk1.value.Bytes()
for i, b := range internal.ReverseScalarBytes(out[:]) {
if skBytes[i] != b {
t.Errorf(
"Expected secret keys to be equal but are different at offset %d: %v != %v",
i,
skBytes[i],
b,
)
}
}
sk2 := new(SecretKey)
err = sk2.UnmarshalBinary(skBytes)
if err != nil {
t.Errorf("Expected FromBytes to succeed but failed.")
}
if !bytes.Equal(marshalStruct(sk2, t), skBytes) {
t.Errorf("Expected secret keys to be equal but are different")
}
}
// Verifies that the thresholdize creates the expected number
// of shares
func TestThresholdizeSecretKeyCountsCorrect(t *testing.T) {
sk := &SecretKey{value: bls12381.Bls12381FqNew().SetBigInt(big.NewInt(248631463258962596))}
tests := []struct {
key *SecretKey
t, n uint
expectedError bool
}{
// bad cases
{sk, 1, 1, true}, // n == 1
{sk, 1, 5, true}, // t == 1
{nil, 3, 5, true}, // sk nil
{sk, 101, 100, true}, // t> n
{sk, 0, 10, true}, // t == 0
{sk, 10, 256, true}, // n > 256
// good cases
{sk, 10, 10, false}, // t == n
{sk, 2, 10, false}, // boundary case for t
{sk, 9, 10, false}, // boundary case for t
{sk, 10, 255, false}, // boundary case for n
{sk, 254, 255, false}, // boundary case for t,n
{sk, 100, 200, false}, // arbitrary t,n values
{sk, 10, 20, false}, // arbitrary t,n values
{sk, 15, 200, false}, // arbitrary t,n values
{sk, 254, 255, false}, // boundary case
{sk, 255, 255, false}, // boundary case
}
// Run all the tests!
for i, test := range tests {
shares, err := thresholdizeSecretKey(test.key, test.t, test.n)
// Check for errors
if test.expectedError && err == nil {
t.Errorf(
"%d - expected an error but received nil. t=%v, n=%v, sk=%v",
i,
test.t,
test.n,
sk,
)
}
// Check for errors
if !test.expectedError && err != nil {
t.Errorf(
"%d - received unexpected error %v. t=%v, n=%v, sk=%v",
i,
err,
test.t,
test.n,
sk,
)
}
// Check the share count == n
if !test.expectedError && test.n != uint(len(shares)) {
t.Errorf("%d - expected len(shares) = %v != %v (n)", i, len(shares), test.n)
}
}
}
func TestSecretKeyShareUnmarshalBinary(t *testing.T) {
sk := genSecretKey(t)
sks, err := thresholdizeSecretKey(sk, 3, 5)
if err != nil {
t.Errorf("Expected thresholdizeSecretKey to pass but failed.")
}
for i, sh := range sks {
b1, err := sh.MarshalBinary()
if err != nil {
t.Errorf("%d - expected MarshalBinary to pass but failed. sh=%v", i, sh)
}
// UnmarshalBinary b1 to new SecretKeyShare
sh1 := new(SecretKeyShare)
err = sh1.UnmarshalBinary(b1)
if err != nil {
t.Errorf("%d - expected UnmarshalBinary to pass but failed. sh=%v", i, sh)
}
// zero bytes slice with length equal to SecretKeySize
zeros := make([]byte, SecretKeySize)
b2, err := sh1.MarshalBinary()
if err != nil {
t.Errorf("%d - expected MarshalBinary to pass but failed. sh1=%v", i, sh1)
}
// Check if []bytes from UnmarshalBinary != zeros && Initial bytes(b1) == Final bytes(b2)
if bytes.Equal(zeros, b2) && !bytes.Equal(b1, b2) {
t.Errorf(
"%d - expected UnmarshalBinary to give non zeros value but failed. sh1=%v, sh=%v",
i,
sh1,
sh,
)
}
}
}
+484
View File
@@ -0,0 +1,484 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"fmt"
)
const (
// Domain separation tag for basic signatures
// according to section 4.2.1 in
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
blsSignatureBasicVtDst = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_"
// Domain separation tag for basic signatures
// according to section 4.2.2 in
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
blsSignatureAugVtDst = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_AUG_"
// Domain separation tag for proof of possession signatures
// according to section 4.2.3 in
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
blsSignaturePopVtDst = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_POP_"
// Domain separation tag for proof of possession proofs
// according to section 4.2.3 in
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
blsPopProofVtDst = "BLS_POP_BLS12381G1_XMD:SHA-256_SSWU_RO_POP_"
)
type BlsSchemeVt interface {
Keygen() (*PublicKeyVt, *SecretKey, error)
KeygenWithSeed(ikm []byte) (*PublicKeyVt, *SecretKey, error)
Sign(sk *SecretKey, msg []byte) (*SignatureVt, error)
Verify(pk *PublicKeyVt, msg []byte, sig *SignatureVt) bool
AggregateVerify(pks []*PublicKeyVt, msgs [][]byte, sigs []*SignatureVt) bool
}
// generateKeysVt creates 32 bytes of random data to be fed to
// generateKeysWithSeedVt
func generateKeysVt() (*PublicKeyVt, *SecretKey, error) {
ikm, err := generateRandBytes(32)
if err != nil {
return nil, nil, err
}
return generateKeysWithSeedVt(ikm)
}
// generateKeysWithSeedVt generates a BLS key pair given input key material (ikm)
func generateKeysWithSeedVt(ikm []byte) (*PublicKeyVt, *SecretKey, error) {
sk, err := new(SecretKey).Generate(ikm)
if err != nil {
return nil, nil, err
}
pk, err := sk.GetPublicKeyVt()
if err != nil {
return nil, nil, err
}
return pk, sk, nil
}
// thresholdGenerateKeys will generate random secret key shares and the corresponding public key
func thresholdGenerateKeysVt(threshold, total uint) (*PublicKeyVt, []*SecretKeyShare, error) {
pk, sk, err := generateKeysVt()
if err != nil {
return nil, nil, err
}
shares, err := thresholdizeSecretKey(sk, threshold, total)
if err != nil {
return nil, nil, err
}
return pk, shares, nil
}
// thresholdGenerateKeysWithSeed will generate random secret key shares and the corresponding public key
// using the corresponding seed `ikm`
func thresholdGenerateKeysWithSeedVt(
ikm []byte,
threshold, total uint,
) (*PublicKeyVt, []*SecretKeyShare, error) {
pk, sk, err := generateKeysWithSeedVt(ikm)
if err != nil {
return nil, nil, err
}
shares, err := thresholdizeSecretKey(sk, threshold, total)
if err != nil {
return nil, nil, err
}
return pk, shares, nil
}
// SigBasicVt is minimal-pubkey-size scheme that doesn't support FastAggregateVerification.
// see: https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03#section-4.2.1
type SigBasicVt struct {
dst string
}
// Creates a new BLS basic signature scheme with the standard domain separation tag used for signatures.
func NewSigBasicVt() *SigBasicVt {
return &SigBasicVt{dst: blsSignatureBasicVtDst}
}
// Creates a new BLS basic signature scheme with a custom domain separation tag used for signatures.
func NewSigBasicVtWithDst(signDst string) *SigBasicVt {
return &SigBasicVt{dst: signDst}
}
// Creates a new BLS key pair
func (b SigBasicVt) Keygen() (*PublicKeyVt, *SecretKey, error) {
return generateKeysVt()
}
// Creates a new BLS key pair
// Input key material (ikm) MUST be at least 32 bytes long,
// but it MAY be longer.
func (b SigBasicVt) KeygenWithSeed(ikm []byte) (*PublicKeyVt, *SecretKey, error) {
return generateKeysWithSeedVt(ikm)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigBasicVt) ThresholdKeygen(
threshold, total uint,
) (*PublicKeyVt, []*SecretKeyShare, error) {
return thresholdGenerateKeysVt(threshold, total)
}
// ThresholdKeygenWithSeed generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures from input key material (ikm)
func (b SigBasicVt) ThresholdKeygenWithSeed(
ikm []byte,
threshold, total uint,
) (*PublicKeyVt, []*SecretKeyShare, error) {
return thresholdGenerateKeysWithSeedVt(ikm, threshold, total)
}
// Computes a signature in G1 from sk, a secret key, and a message
func (b SigBasicVt) Sign(sk *SecretKey, msg []byte) (*SignatureVt, error) {
return sk.createSignatureVt(msg, b.dst)
}
// Compute a partial signature in G2 that can be combined with other partial signature
func (b SigBasicVt) PartialSign(sks *SecretKeyShare, msg []byte) (*PartialSignatureVt, error) {
return sks.partialSignVt(msg, b.dst)
}
// CombineSignatures takes partial signatures to yield a completed signature
func (b SigBasicVt) CombineSignatures(sigs ...*PartialSignatureVt) (*SignatureVt, error) {
return combineSigsVt(sigs)
}
// Checks that a signature is valid for the message under the public key pk
func (b SigBasicVt) Verify(pk *PublicKeyVt, msg []byte, sig *SignatureVt) (bool, error) {
return pk.verifySignatureVt(msg, sig, b.dst)
}
// The AggregateVerify algorithm checks an aggregated signature over
// several (PK, message, signature) pairs.
// Each message must be different or this will return false.
// See section 3.1.1 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigBasicVt) AggregateVerify(
pks []*PublicKeyVt,
msgs [][]byte,
sigs []*SignatureVt,
) (bool, error) {
if !allRowsUnique(msgs) {
return false, fmt.Errorf("all messages must be distinct")
}
asig, err := aggregateSignaturesVt(sigs...)
if err != nil {
return false, err
}
return asig.aggregateVerify(pks, msgs, b.dst)
}
// SigAugVt is minimal-signature-size scheme that doesn't support FastAggregateVerification.
// see: https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03#section-4.2.2
type SigAugVt struct {
dst string
}
// Creates a new BLS message augmentation signature scheme with the standard domain separation tag used for signatures.
func NewSigAugVt() *SigAugVt {
return &SigAugVt{dst: blsSignatureAugVtDst}
}
// Creates a new BLS message augmentation signature scheme with a custom domain separation tag used for signatures.
func NewSigAugVtWithDst(signDst string) *SigAugVt {
return &SigAugVt{dst: signDst}
}
// Creates a new BLS key pair
func (b SigAugVt) Keygen() (*PublicKeyVt, *SecretKey, error) {
return generateKeysVt()
}
// Creates a new BLS secret key
// Input key material (ikm) MUST be at least 32 bytes long,
// but it MAY be longer.
func (b SigAugVt) KeygenWithSeed(ikm []byte) (*PublicKeyVt, *SecretKey, error) {
return generateKeysWithSeedVt(ikm)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigAugVt) ThresholdKeygen(threshold, total uint) (*PublicKeyVt, []*SecretKeyShare, error) {
return thresholdGenerateKeysVt(threshold, total)
}
// ThresholdKeygenWithSeed generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigAugVt) ThresholdKeygenWithSeed(
ikm []byte,
threshold, total uint,
) (*PublicKeyVt, []*SecretKeyShare, error) {
return thresholdGenerateKeysWithSeedVt(ikm, threshold, total)
}
// Computes a signature in G1 from sk, a secret key, and a message
// See section 3.2.1 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02
func (b SigAugVt) Sign(sk *SecretKey, msg []byte) (*SignatureVt, error) {
pk, err := sk.GetPublicKeyVt()
if err != nil {
return nil, err
}
bytes, err := pk.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("MarshalBinary failed")
}
bytes = append(bytes, msg...)
return sk.createSignatureVt(bytes, b.dst)
}
// Compute a partial signature in G2 that can be combined with other partial signature
func (b SigAugVt) PartialSign(
sks *SecretKeyShare,
pk *PublicKeyVt,
msg []byte,
) (*PartialSignatureVt, error) {
if len(msg) == 0 {
return nil, fmt.Errorf("message cannot be empty or nil")
}
bytes, err := pk.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("MarshalBinary failed")
}
bytes = append(bytes, msg...)
return sks.partialSignVt(bytes, b.dst)
}
// CombineSignatures takes partial signatures to yield a completed signature
func (b SigAugVt) CombineSignatures(sigs ...*PartialSignatureVt) (*SignatureVt, error) {
return combineSigsVt(sigs)
}
// Checks that a signature is valid for the message under the public key pk
// See section 3.2.2 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigAugVt) Verify(pk *PublicKeyVt, msg []byte, sig *SignatureVt) (bool, error) {
bytes, err := pk.MarshalBinary()
if err != nil {
return false, err
}
bytes = append(bytes, msg...)
return pk.verifySignatureVt(bytes, sig, b.dst)
}
// The aggregateVerify algorithm checks an aggregated signature over
// several (PK, message, signature) pairs.
// See section 3.2.3 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigAugVt) AggregateVerify(
pks []*PublicKeyVt,
msgs [][]byte,
sigs []*SignatureVt,
) (bool, error) {
if len(pks) != len(msgs) {
return false, fmt.Errorf(
"the number of public keys does not match the number of messages: %v != %v",
len(pks),
len(msgs),
)
}
data := make([][]byte, len(msgs))
for i, msg := range msgs {
bytes, err := pks[i].MarshalBinary()
if err != nil {
return false, err
}
data[i] = append(bytes, msg...)
}
asig, err := aggregateSignaturesVt(sigs...)
if err != nil {
return false, err
}
return asig.aggregateVerify(pks, data, b.dst)
}
// SigEth2Vt supports signatures on Eth2.
// Internally is an alias for SigPopVt
type SigEth2Vt = SigPopVt
// NewSigEth2Vt Creates a new BLS ETH2 signature scheme with the standard domain separation tag used for signatures.
func NewSigEth2Vt() *SigEth2Vt {
return NewSigPopVt()
}
// SigPopVt is minimal-signature-size scheme that supports FastAggregateVerification
// and requires using proofs of possession to mitigate rogue-key attacks
// see: https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03#section-4.2.3
type SigPopVt struct {
sigDst string
popDst string
}
// Creates a new BLS proof of possession signature scheme with the standard domain separation tag used for signatures.
func NewSigPopVt() *SigPopVt {
return &SigPopVt{sigDst: blsSignaturePopVtDst, popDst: blsPopProofVtDst}
}
// Creates a new BLS message proof of possession signature scheme with a custom domain separation tag used for signatures.
func NewSigPopVtWithDst(signDst, popDst string) (*SigPopVt, error) {
if signDst == popDst {
return nil, fmt.Errorf("domain separation tags cannot be equal")
}
return &SigPopVt{sigDst: signDst, popDst: popDst}, nil
}
// Creates a new BLS key pair
func (b SigPopVt) Keygen() (*PublicKeyVt, *SecretKey, error) {
return generateKeysVt()
}
// Creates a new BLS secret key
// Input key material (ikm) MUST be at least 32 bytes long,
// but it MAY be longer.
func (b SigPopVt) KeygenWithSeed(ikm []byte) (*PublicKeyVt, *SecretKey, error) {
return generateKeysWithSeedVt(ikm)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigPopVt) ThresholdKeygen(threshold, total uint) (*PublicKeyVt, []*SecretKeyShare, error) {
return thresholdGenerateKeysVt(threshold, total)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigPopVt) ThresholdKeygenWithSeed(
ikm []byte,
threshold, total uint,
) (*PublicKeyVt, []*SecretKeyShare, error) {
return thresholdGenerateKeysWithSeedVt(ikm, threshold, total)
}
// Computes a signature in G1 from sk, a secret key, and a message
// See section 2.6 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPopVt) Sign(sk *SecretKey, msg []byte) (*SignatureVt, error) {
return sk.createSignatureVt(msg, b.sigDst)
}
// Compute a partial signature in G2 that can be combined with other partial signature
func (b SigPopVt) PartialSign(sks *SecretKeyShare, msg []byte) (*PartialSignatureVt, error) {
return sks.partialSignVt(msg, b.sigDst)
}
// CombineSignatures takes partial signatures to yield a completed signature
func (b SigPopVt) CombineSignatures(sigs ...*PartialSignatureVt) (*SignatureVt, error) {
return combineSigsVt(sigs)
}
// Checks that a signature is valid for the message under the public key pk
// See section 2.7 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPopVt) Verify(pk *PublicKeyVt, msg []byte, sig *SignatureVt) (bool, error) {
return pk.verifySignatureVt(msg, sig, b.sigDst)
}
// The aggregateVerify algorithm checks an aggregated signature over
// several (PK, message, signature) pairs.
// Each message must be different or this will return false.
// See section 3.1.1 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02
func (b SigPopVt) AggregateVerify(
pks []*PublicKeyVt,
msgs [][]byte,
sigs []*SignatureVt,
) (bool, error) {
if !allRowsUnique(msgs) {
return false, fmt.Errorf("all messages must be distinct")
}
asig, err := aggregateSignaturesVt(sigs...)
if err != nil {
return false, err
}
return asig.aggregateVerify(pks, msgs, b.sigDst)
}
// Combine many signatures together to form a Multisignature.
// Multisignatures can be created when multiple signers jointly
// generate signatures over the same message.
func (b SigPopVt) AggregateSignatures(sigs ...*SignatureVt) (*MultiSignatureVt, error) {
g1, err := aggregateSignaturesVt(sigs...)
if err != nil {
return nil, err
}
return &MultiSignatureVt{value: g1.value}, nil
}
// Combine many public keys together to form a Multipublickey.
// Multipublickeys are used to verify multisignatures.
func (b SigPopVt) AggregatePublicKeys(pks ...*PublicKeyVt) (*MultiPublicKeyVt, error) {
g2, err := aggregatePublicKeysVt(pks...)
if err != nil {
return nil, err
}
return &MultiPublicKeyVt{value: g2.value}, nil
}
// Checks that a multisignature is valid for the message under the multi public key
// Similar to FastAggregateVerify except the keys and signatures have already been
// combined. See section 3.3.4 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-02
func (b SigPopVt) VerifyMultiSignature(
pk *MultiPublicKeyVt,
msg []byte,
sig *MultiSignatureVt,
) (bool, error) {
s := &SignatureVt{value: sig.value}
p := &PublicKeyVt{value: pk.value}
return p.verifySignatureVt(msg, s, b.sigDst)
}
// FastAggregateVerify verifies an aggregated signature over the same message under the given public keys.
// See section 3.3.4 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPopVt) FastAggregateVerify(
pks []*PublicKeyVt,
msg []byte,
asig *SignatureVt,
) (bool, error) {
apk, err := aggregatePublicKeysVt(pks...)
if err != nil {
return false, err
}
return apk.verifySignatureVt(msg, asig, b.sigDst)
}
// FastAggregateVerifyConstituent verifies a list of signature over the same message under the given public keys.
// See section 3.3.4 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPopVt) FastAggregateVerifyConstituent(
pks []*PublicKeyVt,
msg []byte,
sigs []*SignatureVt,
) (bool, error) {
asig, err := aggregateSignaturesVt(sigs...)
if err != nil {
return false, err
}
return b.FastAggregateVerify(pks, msg, asig)
}
// Create a proof of possession for the corresponding public key.
// A proof of possession must be created for each public key to be used
// in FastAggregateVerify or a Multipublickey to avoid rogue key attacks.
// See section 3.3.2 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPopVt) PopProve(sk *SecretKey) (*ProofOfPossessionVt, error) {
return sk.createProofOfPossessionVt(b.popDst)
}
// verify a proof of possession for the corresponding public key is valid.
// A proof of possession must be created for each public key to be used
// in FastAggregateVerify or a Multipublickey to avoid rogue key attacks.
// See section 3.3.3 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPopVt) PopVerify(pk *PublicKeyVt, pop1 *ProofOfPossessionVt) (bool, error) {
return pop1.verify(pk, b.popDst)
}
+516
View File
@@ -0,0 +1,516 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"fmt"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
"github.com/sonr-io/sonr/crypto/internal"
)
// Implement BLS signatures on the BLS12-381 curve
// according to https://crypto.standford.edu/~dabo/pubs/papers/BLSmultisig.html
// and https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
// this file implements signatures in G1 and public keys in G2.
// Public Keys and Signatures can be aggregated but the consumer
// must use proofs of possession to defend against rogue-key attacks.
const (
// Public key size in G2
PublicKeyVtSize = 96
// Signature size in G1
SignatureVtSize = 48
// Proof of Possession in G1
ProofOfPossessionVtSize = 48
)
// Represents a public key in G2
type PublicKeyVt struct {
value bls12381.G2
}
// Serialize a public key to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (pk *PublicKeyVt) MarshalBinary() ([]byte, error) {
out := pk.value.ToCompressed()
return out[:], nil
}
// Deserialize a public key from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the public key
// otherwise it will return an error
func (pk *PublicKeyVt) UnmarshalBinary(data []byte) error {
if len(data) != PublicKeyVtSize {
return fmt.Errorf("public key must be %d bytes", PublicKeySize)
}
var blob [PublicKeyVtSize]byte
copy(blob[:], data)
p2, err := new(bls12381.G2).FromCompressed(&blob)
if err != nil {
return err
}
if p2.IsIdentity() == 1 {
return fmt.Errorf("public keys cannot be zero")
}
pk.value = *p2
return nil
}
// Represents a BLS signature in G1
type SignatureVt struct {
value bls12381.G1
}
// Serialize a signature to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (sig *SignatureVt) MarshalBinary() ([]byte, error) {
out := sig.value.ToCompressed()
return out[:], nil
}
func (sig *SignatureVt) verify(pk *PublicKeyVt, message []byte, signDstVt string) (bool, error) {
return pk.verifySignatureVt(message, sig, signDstVt)
}
// The AggregateVerify algorithm checks an aggregated signature over
// several (PK, message) pairs.
// The Signature is the output of aggregateSignaturesVt
// Each message must be different or this will return false.
// See section 3.1.1 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (sig *SignatureVt) aggregateVerify(
pks []*PublicKeyVt,
msgs [][]byte,
signDstVt string,
) (bool, error) {
return sig.coreAggregateVerify(pks, msgs, signDstVt)
}
func (sig *SignatureVt) coreAggregateVerify(
pks []*PublicKeyVt,
msgs [][]byte,
signDstVt string,
) (bool, error) {
if len(pks) < 1 {
return false, fmt.Errorf("at least one key is required")
}
if len(msgs) < 1 {
return false, fmt.Errorf("at least one message is required")
}
if len(pks) != len(msgs) {
return false, fmt.Errorf(
"the number of public keys does not match the number of messages: %v != %v",
len(pks),
len(msgs),
)
}
if sig.value.InCorrectSubgroup() == 0 {
return false, fmt.Errorf("signature is not in the correct subgroup")
}
engine := new(bls12381.Engine)
dst := []byte(signDstVt)
// e(H(m_1), pk_1)*...*e(H(m_N), pk_N) == e(s, g2)
// However, we use only one miller loop
// by doing the equivalent of
// e(H(m_1), pk_1)*...*e(H(m_N), pk_N) * e(s^-1, g2) == 1
for i, pk := range pks {
if pk == nil {
return false, fmt.Errorf("public key at %d is nil", i)
}
if pk.value.IsIdentity() == 1 || pk.value.InCorrectSubgroup() == 0 {
return false, fmt.Errorf("public key at %d is not in the correct subgroup", i)
}
p1 := new(bls12381.G1).Hash(native.EllipticPointHasherSha256(), msgs[i], dst)
engine.AddPair(p1, &pk.value)
}
engine.AddPairInvG2(&sig.value, new(bls12381.G2).Generator())
return engine.Check(), nil
}
// Deserialize a signature from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the Signature
// otherwise it will return an error
func (sig *SignatureVt) UnmarshalBinary(data []byte) error {
if len(data) != SignatureVtSize {
return fmt.Errorf("signature must be %d bytes", SignatureSize)
}
var blob [SignatureVtSize]byte
copy(blob[:], data)
p1, err := new(bls12381.G1).FromCompressed(&blob)
if err != nil {
return err
}
if p1.IsIdentity() == 1 {
return fmt.Errorf("signatures cannot be zero")
}
sig.value = *p1
return nil
}
// Get the corresponding public key from a secret key
// Verifies the public key is in the correct subgroup
func (sk *SecretKey) GetPublicKeyVt() (*PublicKeyVt, error) {
result := new(bls12381.G2).Mul(new(bls12381.G2).Generator(), sk.value)
if result.InCorrectSubgroup() == 0 || result.IsIdentity() == 1 {
return nil, fmt.Errorf("point is not in correct subgroup")
}
return &PublicKeyVt{value: *result}, nil
}
// Compute a signature from a secret key and message
// This signature is deterministic which protects against
// attacks arising from signing with bad randomness like
// the nonce reuse attack on ECDSA. `message` is
// hashed to a point in G1 as described in to
// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1
// See Section 2.6 in https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
// nil message is not permitted but empty slice is allowed
func (sk *SecretKey) createSignatureVt(message []byte, dstVt string) (*SignatureVt, error) {
if message == nil {
return nil, fmt.Errorf("message cannot be nil")
}
if sk.value.IsZero() == 1 {
return nil, fmt.Errorf("invalid secret key")
}
p1 := new(bls12381.G1).Hash(native.EllipticPointHasherSha256(), message, []byte(dstVt))
result := new(bls12381.G1).Mul(p1, sk.value)
if result.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("point is not in correct subgroup")
}
return &SignatureVt{value: *result}, nil
}
// Verify a signature is valid for the message under this public key.
// See Section 2.7 in https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (pk PublicKeyVt) verifySignatureVt(
message []byte,
signature *SignatureVt,
dstVt string,
) (bool, error) {
if signature == nil || message == nil || pk.value.IsIdentity() == 1 {
return false, fmt.Errorf("signature and message and public key cannot be nil or zero")
}
if signature.value.IsIdentity() == 1 || signature.value.InCorrectSubgroup() == 0 {
return false, fmt.Errorf("signature is not in the correct subgroup")
}
engine := new(bls12381.Engine)
p1 := new(bls12381.G1).Hash(native.EllipticPointHasherSha256(), message, []byte(dstVt))
// e(H(m), pk) == e(s, g2)
// However, we can reduce the number of miller loops
// by doing the equivalent of
// e(H(m)^-1, pk) * e(s, g2) == 1
engine.AddPairInvG1(p1, &pk.value)
engine.AddPair(&signature.value, new(bls12381.G2).Generator())
return engine.Check(), nil
}
// Combine public keys into one aggregated key
func aggregatePublicKeysVt(pks ...*PublicKeyVt) (*PublicKeyVt, error) {
if len(pks) < 1 {
return nil, fmt.Errorf("at least one public key is required")
}
result := new(bls12381.G2).Identity()
for i, k := range pks {
if k == nil {
return nil, fmt.Errorf("key at %d is nil, keys cannot be nil", i)
}
if k.value.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("key at %d is not in the correct subgroup", i)
}
result.Add(result, &k.value)
}
return &PublicKeyVt{value: *result}, nil
}
// Combine signatures into one aggregated signature
func aggregateSignaturesVt(sigs ...*SignatureVt) (*SignatureVt, error) {
if len(sigs) < 1 {
return nil, fmt.Errorf("at least one signature is required")
}
result := new(bls12381.G1).Identity()
for i, s := range sigs {
if s == nil {
return nil, fmt.Errorf("signature at %d is nil, signature cannot be nil", i)
}
if s.value.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("signature at %d is not in the correct subgroup", i)
}
result.Add(result, &s.value)
}
return &SignatureVt{value: *result}, nil
}
// A proof of possession scheme uses a separate public key validation
// step, called a proof of possession, to defend against rogue key
// attacks. This enables an optimization to aggregate signature
// verification for the case that all signatures are on the same
// message.
type ProofOfPossessionVt struct {
value bls12381.G1
}
// Generates a proof-of-possession (PoP) for this secret key. The PoP signature should be verified before
// before accepting any aggregate signatures related to the corresponding pubkey.
func (sk *SecretKey) createProofOfPossessionVt(popDstVt string) (*ProofOfPossessionVt, error) {
pk, err := sk.GetPublicKeyVt()
if err != nil {
return nil, err
}
msg, err := pk.MarshalBinary()
if err != nil {
return nil, err
}
sig, err := sk.createSignatureVt(msg, popDstVt)
if err != nil {
return nil, err
}
return &ProofOfPossessionVt{value: sig.value}, nil
}
// Serialize a proof of possession to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (pop *ProofOfPossessionVt) MarshalBinary() ([]byte, error) {
out := pop.value.ToCompressed()
return out[:], nil
}
// Deserialize a proof of possession from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the Signature
// otherwise it will return an error
func (pop *ProofOfPossessionVt) UnmarshalBinary(data []byte) error {
p1 := new(SignatureVt)
err := p1.UnmarshalBinary(data)
if err != nil {
return err
}
pop.value = p1.value
return nil
}
// Verifies that PoP is valid for this pubkey. In order to prevent rogue key attacks, a PoP must be validated
// for each pubkey in an aggregated signature.
func (pop *ProofOfPossessionVt) verify(pk *PublicKeyVt, popDstVt string) (bool, error) {
if pk == nil {
return false, fmt.Errorf("public key cannot be nil")
}
msg, err := pk.MarshalBinary()
if err != nil {
return false, err
}
return pk.verifySignatureVt(msg, &SignatureVt{value: pop.value}, popDstVt)
}
// Represents an MultiSignature in G1. A multisignature is used when multiple signatures
// are calculated over the same message vs an aggregate signature where each message signed
// is a unique.
type MultiSignatureVt struct {
value bls12381.G1
}
// Serialize a multi-signature to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (sig *MultiSignatureVt) MarshalBinary() ([]byte, error) {
out := sig.value.ToCompressed()
return out[:], nil
}
// Check a multisignature is valid for a multipublickey and a message
func (sig *MultiSignatureVt) verify(
pk *MultiPublicKeyVt,
message []byte,
signDstVt string,
) (bool, error) {
if pk == nil {
return false, fmt.Errorf("public key cannot be nil")
}
p := &PublicKeyVt{value: pk.value}
return p.verifySignatureVt(message, &SignatureVt{value: sig.value}, signDstVt)
}
// Deserialize a signature from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the Signature
// otherwise it will return an error
func (sig *MultiSignatureVt) UnmarshalBinary(data []byte) error {
if len(data) != SignatureVtSize {
return fmt.Errorf("multi signature must be %v bytes", SignatureSize)
}
s1 := new(SignatureVt)
err := s1.UnmarshalBinary(data)
if err != nil {
return err
}
sig.value = s1.value
return nil
}
// Represents accumulated multiple Public Keys in G2 for verifying a multisignature
type MultiPublicKeyVt struct {
value bls12381.G2
}
// Serialize a public key to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (pk *MultiPublicKeyVt) MarshalBinary() ([]byte, error) {
out := pk.value.ToCompressed()
return out[:], nil
}
// Deserialize a public key from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the public key
// otherwise it will return an error
func (pk *MultiPublicKeyVt) UnmarshalBinary(data []byte) error {
if len(data) != PublicKeyVtSize {
return fmt.Errorf("multi public key must be %v bytes", PublicKeySize)
}
p2 := new(PublicKeyVt)
err := p2.UnmarshalBinary(data)
if err != nil {
return err
}
pk.value = p2.value
return nil
}
// Check a multisignature is valid for a multipublickey and a message
func (pk *MultiPublicKeyVt) verify(
message []byte,
sig *MultiSignatureVt,
signDstVt string,
) (bool, error) {
return sig.verify(pk, message, signDstVt)
}
// PartialSignatureVt represents threshold Gap Diffie-Hellman BLS signature
// that can be combined with other partials to yield a completed BLS signature
// See section 3.2 in <https://www.cc.gatech.edu/~aboldyre/papers/bold.pdf>
type PartialSignatureVt struct {
identifier byte
signature bls12381.G1
}
// partialSignVt creates a partial signature that can be combined with other partial signatures
// to yield a complete signature
func (sks *SecretKeyShare) partialSignVt(
message []byte,
signDst string,
) (*PartialSignatureVt, error) {
if len(message) == 0 {
return nil, fmt.Errorf("message cannot be empty or nil")
}
p1 := new(bls12381.G1).Hash(native.EllipticPointHasherSha256(), message, []byte(signDst))
var blob [SecretKeySize]byte
copy(blob[:], internal.ReverseScalarBytes(sks.value))
s, err := bls12381.Bls12381FqNew().SetBytes(&blob)
if err != nil {
return nil, err
}
result := new(bls12381.G1).Mul(p1, s)
if result.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("point is not on correct subgroup")
}
return &PartialSignatureVt{identifier: sks.identifier, signature: *result}, nil
}
// combineSigsVt gathers partial signatures and yields a complete signature
func combineSigsVt(partials []*PartialSignatureVt) (*SignatureVt, error) {
if len(partials) < 2 {
return nil, fmt.Errorf("must have at least 2 partial signatures")
}
if len(partials) > 255 {
return nil, fmt.Errorf("unsupported to combine more than 255 signatures")
}
xVars, yVars, err := splitXYVt(partials)
if err != nil {
return nil, err
}
sTmp := new(bls12381.G1).Identity()
sig := new(bls12381.G1).Identity()
// Lagrange interpolation
basis := bls12381.Bls12381FqNew()
for i, xi := range xVars {
basis.SetOne()
for j, xj := range xVars {
if i == j {
continue
}
num := bls12381.Bls12381FqNew().Neg(xj) // x - x_m
den := bls12381.Bls12381FqNew().Sub(xi, xj) // x_j - x_m
_, wasInverted := den.Invert(den)
// wasInverted == false if den == 0
if !wasInverted {
return nil, fmt.Errorf("signatures cannot be recombined")
}
basis.Mul(basis, num.Mul(num, den))
}
sTmp.Mul(yVars[i], basis)
sig.Add(sig, sTmp)
}
if sig.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("signature is not in the correct subgroup")
}
return &SignatureVt{value: *sig}, nil
}
// Ensure no duplicates x values and convert x values to field elements
func splitXYVt(partials []*PartialSignatureVt) ([]*native.Field, []*bls12381.G1, error) {
x := make([]*native.Field, len(partials))
y := make([]*bls12381.G1, len(partials))
dup := make(map[byte]bool)
for i, sp := range partials {
if sp == nil {
return nil, nil, fmt.Errorf("partial signature cannot be nil")
}
if _, exists := dup[sp.identifier]; exists {
return nil, nil, fmt.Errorf("duplicate signature included")
}
if sp.signature.InCorrectSubgroup() == 0 {
return nil, nil, fmt.Errorf("signature is not in the correct subgroup")
}
dup[sp.identifier] = true
x[i] = bls12381.Bls12381FqNew().SetUint64(uint64(sp.identifier))
y[i] = &sp.signature
}
return x, y, nil
}
@@ -0,0 +1,389 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"testing"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
)
func generateAugSignatureG1(sk *SecretKey, msg []byte, t *testing.T) *SignatureVt {
bls := NewSigAugVt()
sig, err := bls.Sign(sk, msg)
if err != nil {
t.Errorf("Aug Sign failed")
}
return sig
}
func generateAugAggregateDataG1(t *testing.T) ([]*PublicKeyVt, []*SignatureVt, [][]byte) {
msgs := make([][]byte, numAggregateG1)
pks := make([]*PublicKeyVt, numAggregateG1)
sigs := make([]*SignatureVt, numAggregateG1)
ikm := make([]byte, 32)
bls := NewSigAugVt()
for i := 0; i < numAggregateG1; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
msg := make([]byte, 20)
readRand(msg, t)
sig := generateAugSignatureG1(sk, msg, t)
msgs[i] = msg
sigs[i] = sig
pks[i] = pk
}
return pks, sigs, msgs
}
func TestAugKeyGenG1Works(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAugVt()
_, _, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
}
func TestAugKeyGenG1Fail(t *testing.T) {
ikm := make([]byte, 10)
readRand(ikm, t)
bls := NewSigAugVt()
_, _, err := bls.KeygenWithSeed(ikm)
if err == nil {
t.Errorf("Aug KeyGen succeeded when it should've failed")
}
}
func TestAugCustomDstG1(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAugVtWithDst("BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_AUG_TEST")
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug Custom Dst KeyGen failed")
}
readRand(ikm, t)
sig, err := bls.Sign(sk, ikm)
if err != nil {
t.Errorf("Aug Custom Dst Sign failed")
}
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Aug Custom Dst Verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Aug Custom Dst Verify succeeded when it should've failed.")
}
}
func TestAugSigningG1(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAugVt()
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
readRand(ikm, t)
sig := generateAugSignatureG1(sk, ikm, t)
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Aug Verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Aug Verify succeeded when it should've failed.")
}
}
func TestAugAggregateVerifyG1Works(t *testing.T) {
pks, sigs, msgs := generateAugAggregateDataG1(t)
bls := NewSigAugVt()
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug AggregateVerify failed")
}
}
func TestAugAggregateVerifyG1BadPks(t *testing.T) {
bls := NewSigAugVt()
pks, sigs, msgs := generateAugAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug AggregateVerify failed")
}
pks[0] = pks[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug AggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
pkValue := new(bls12381.G2).Identity()
pks[0] = &PublicKeyVt{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug AggregateVerify succeeded with zero byte public key it should've failed")
}
// Try with base generator
pkValue.Generator()
pks[0] = &PublicKeyVt{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf(
"Aug aggregateVerify succeeded with the base generator public key it should've failed",
)
}
}
func TestAugAggregateVerifyG1BadSigs(t *testing.T) {
bls := NewSigAugVt()
pks, sigs, msgs := generateAugAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug aggregateVerify failed")
}
sigs[0] = sigs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
// Try a zero signature to make sure it doesn't crash
sigValue := new(bls12381.G1).Identity()
sigs[0] = &SignatureVt{value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded with zero byte signature it should've failed")
}
// Try with base generator
sigValue.Generator()
sigs[0] = &SignatureVt{value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf(
"Aug aggregateVerify succeeded with the base generator signature it should've failed",
)
}
}
func TestAugAggregateVerifyG1BadMsgs(t *testing.T) {
bls := NewSigAugVt()
pks, sigs, msgs := generateAugAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug aggregateVerify failed")
}
// Test len(pks) != len(msgs)
if res, _ := bls.AggregateVerify(pks, msgs[0:8], sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
msgs[0] = msgs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
}
func TestAugAggregateVerifyG1DupMsg(t *testing.T) {
bls := NewSigAugVt()
// Only two messages but repeated
messages := make([][]byte, numAggregateG1)
messages[0] = []byte("Yes")
messages[1] = []byte("No")
for i := 2; i < numAggregateG1; i++ {
messages[i] = messages[i%2]
}
pks := make([]*PublicKeyVt, numAggregateG1)
sigs := make([]*SignatureVt, numAggregateG1)
ikm := make([]byte, 32)
for i := 0; i < numAggregateG1; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
sig := generateAugSignatureG1(sk, messages[i], t)
pks[i] = pk
sigs[i] = sig
}
if res, _ := bls.AggregateVerify(pks, messages, sigs); !res {
t.Errorf("Aug aggregateVerify failed for duplicate messages")
}
}
func TestBlsAugG1KeyGen(t *testing.T) {
bls := NewSigAugVt()
_, _, err := bls.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
}
func TestAugVtThresholdKeygenBadInputs(t *testing.T) {
bls := NewSigAugVt()
_, _, err := bls.ThresholdKeygen(0, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(1, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(3, 2)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
}
func TestAugVtThresholdKeygen(t *testing.T) {
bls := NewSigAugVt()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
if len(sks) != 5 {
t.Errorf("ThresholdKeygen did not produce enough shares")
}
}
func TestAugPartialSignVt(t *testing.T) {
ikm := make([]byte, 32)
bls := NewSigAugVt()
pk, sks, err := bls.ThresholdKeygenWithSeed(ikm, 2, 4)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
msg := make([]byte, 10)
sig1, err := bls.PartialSign(sks[0], pk, msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig2, err := bls.PartialSign(sks[1], pk, msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig, err := bls.CombineSignatures(sig1, sig2)
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("Combined signature does not verify")
}
sig, err = bls.CombineSignatures(sig1)
if err == nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); res {
t.Errorf("Combined signature verify succeeded when it should've failed")
}
}
// Ensure that mixed partial signatures from distinct origins create invalid composite signatures
func TestAugVtPartialMixupShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigAugVt()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
for i := range ikm {
ikm[i] = 1
}
pk2, sks2, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignatureVt, total)
sigs2 := make([]*PartialSignatureVt, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], pk1, msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
sigs2[i], err = bls.PartialSign(sks2[i], pk2, msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining 2 from group 1 and 2 from group 2
sig, err := bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[2], sigs2[3])
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
if res, _ := bls.Verify(pk2, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
// Should error out due to duplicate identifiers
_, err = bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[0], sigs2[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded.")
}
}
func TestEmptyMessageAugPartialSignVt(t *testing.T) {
bls := NewSigAugVt()
pk, sks, err := bls.ThresholdKeygen(5, 6)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
// Sign an empty message
_, err = bls.PartialSign(sks[0], pk, []byte{})
if err == nil {
t.Errorf("Expected partial sign to fail on empty message")
}
}
func TestNilMessageAugPartialSignVt(t *testing.T) {
bls := NewSigAugVt()
pk, sks, err := bls.ThresholdKeygen(5, 6)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
// Sign nil message
_, err = bls.PartialSign(sks[0], pk, nil)
if err == nil {
t.Errorf("Expected partial signing to fail on nil message")
}
}
@@ -0,0 +1,385 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"testing"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
)
func generateBasicSignatureG1(sk *SecretKey, msg []byte, t *testing.T) *SignatureVt {
bls := NewSigBasicVt()
sig, err := bls.Sign(sk, msg)
if err != nil {
t.Errorf("Basic Sign failed")
}
return sig
}
func generateBasicAggregateDataG1(t *testing.T) ([]*PublicKeyVt, []*SignatureVt, [][]byte) {
msgs := make([][]byte, numAggregateG1)
pks := make([]*PublicKeyVt, numAggregateG1)
sigs := make([]*SignatureVt, numAggregateG1)
ikm := make([]byte, 32)
bls := NewSigBasicVt()
for i := 0; i < numAggregateG1; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Basic KeyGen failed")
}
msg := make([]byte, 20)
readRand(msg, t)
sig := generateBasicSignatureG1(sk, msg, t)
msgs[i] = msg
sigs[i] = sig
pks[i] = pk
}
return pks, sigs, msgs
}
func TestBasicKeyGenG1Works(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigBasicVt()
_, _, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Basic KeyGen failed")
}
}
func TestBasicKeyGenG1Fail(t *testing.T) {
ikm := make([]byte, 10)
readRand(ikm, t)
bls := NewSigBasicVt()
_, _, err := bls.KeygenWithSeed(ikm)
if err == nil {
t.Errorf("Basic KeyGen succeeded when it should've failed")
}
}
func TestBasicCustomDstG1(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigBasicVtWithDst("BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_TEST")
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Basic Custom Dst KeyGen failed")
}
readRand(ikm, t)
sig, err := bls.Sign(sk, ikm)
if err != nil {
t.Errorf("Basic Custom Dst Sign failed")
}
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Basic Custon Dst Verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Basic Custom Dst Verify succeeded when it should've failed.")
}
}
func TestBasicSigningG1(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigBasicVt()
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Basic KeyGen failed")
}
readRand(ikm, t)
sig := generateBasicSignatureG1(sk, ikm, t)
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Basic Verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Basic Verify succeeded when it should've failed.")
}
}
func TestBasicSigningEmptyMessage(t *testing.T) {
bls := NewSigBasicVt()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Basic KeyGen failed")
}
// Sign an empty message
_, err = bls.Sign(sk, []byte{})
if err != nil {
t.Errorf("Expected signing message to succeed: %v", err)
}
}
func TestBasicSigningNilMessage(t *testing.T) {
bls := NewSigBasicVt()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Basic KeyGen failed")
}
// Sign nil message
_, err = bls.Sign(sk, nil)
if err == nil {
t.Errorf("Expected signing empty message to fail")
}
}
func TestBasicAggregateVerifyG1Works(t *testing.T) {
pks, sigs, msgs := generateBasicAggregateDataG1(t)
bls := NewSigBasicVt()
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Basic AggregateVerify failed")
}
}
func TestBasicAggregateVerifyG1BadPks(t *testing.T) {
bls := NewSigBasicVt()
pks, sigs, msgs := generateBasicAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Basic aggregateVerify failed")
}
pks[0] = pks[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic aggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
pkValue := new(bls12381.G2).Identity()
pks[0] = &PublicKeyVt{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic aggregateVerify succeeded with zero byte public key it should've failed")
}
// Try with base generator
pkValue.Generator()
pks[0] = &PublicKeyVt{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf(
"Basic aggregateVerify succeeded with the base generator public key it should've failed",
)
}
}
func TestBasicAggregateVerifyG1BadSigs(t *testing.T) {
bls := NewSigBasicVt()
pks, sigs, msgs := generateBasicAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Basic aggregateVerify failed")
}
sigs[0] = sigs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic aggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
g1 := new(bls12381.G1).Identity()
sigValue := g1.Identity()
sigs[0] = &SignatureVt{value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic aggregateVerify succeeded with zero byte signature it should've failed")
}
// Try with base generator
sigValue = g1.Generator()
sigs[0] = &SignatureVt{value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf(
"Basic aggregateVerify succeeded with the base generator signature it should've failed",
)
}
}
func TestBasicAggregateVerifyG1BadMsgs(t *testing.T) {
bls := NewSigBasicVt()
pks, sigs, msgs := generateBasicAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Basic aggregateVerify failed")
}
msgs[0] = msgs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic aggregateVerify succeeded when it should've failed")
}
}
func TestBasicVtThresholdKeygenBadInputs(t *testing.T) {
bls := NewSigBasicVt()
_, _, err := bls.ThresholdKeygen(0, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(0, 1)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(3, 2)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
}
func TestBasicVtThresholdKeygen(t *testing.T) {
bls := NewSigBasicVt()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
if len(sks) != 5 {
t.Errorf("ThresholdKeygen did not produce enough shares")
}
}
func TestBasicPartialSignVt(t *testing.T) {
ikm := make([]byte, 32)
bls := NewSigBasicVt()
pk, sks, err := bls.ThresholdKeygenWithSeed(ikm, 2, 4)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
msg := make([]byte, 10)
sig1, err := bls.PartialSign(sks[0], msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig2, err := bls.PartialSign(sks[1], msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig, err := bls.CombineSignatures(sig1, sig2)
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("Combined signature does not verify")
}
sig, err = bls.CombineSignatures(sig1)
if err == nil {
t.Errorf("CombineSignatures succeeded when it should've failed")
}
if res, _ := bls.Verify(pk, msg, sig); res {
t.Errorf("Combined signature verify succeeded when it should've failed")
}
}
// Ensure that mixed partial signatures from distinct origins create invalid composite signatures
func TestBasicVtPartialMixupShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigBasicVt()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
for i := range ikm {
ikm[i] = 1
}
pk2, sks2, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignatureVt, total)
sigs2 := make([]*PartialSignatureVt, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
sigs2[i], err = bls.PartialSign(sks2[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining 2 from group 1 and 2 from group 2
sig, err := bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[2], sigs2[3])
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
if res, _ := bls.Verify(pk2, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
// Should error out due to duplicate identifiers
_, err = bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[0], sigs2[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded.")
}
}
func TestIdentityPublicKeyVt(t *testing.T) {
bls := NewSigBasicVt()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
msg := []byte{0, 0, 0, 0}
sig, _ := bls.Sign(sk, msg)
pk := PublicKeyVt{value: *new(bls12381.G2).Identity()}
if res, _ := bls.Verify(&pk, msg, sig); res {
t.Errorf("Verify succeeded when the public key is the identity.")
}
}
func TestThresholdSignTooHighAndLowVt(t *testing.T) {
bls := NewSigBasicVt()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
msg := make([]byte, 10)
ps, err := bls.PartialSign(sks[0], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
_, err = bls.CombineSignatures(ps)
if err == nil {
t.Errorf("CombinSignatures succeeded when it should've failed")
}
pss := make([]*PartialSignatureVt, 256)
_, err = bls.CombineSignatures(pss...)
if err == nil {
t.Errorf("CombinSignatures succeeded when it should've failed")
}
}
@@ -0,0 +1,962 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"bytes"
"math/big"
"math/rand"
"testing"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
)
const numAggregateG1 = 10
func TestGetPublicKeyG2(t *testing.T) {
sk := genSecretKey(t)
pk := genPublicKeyVt(sk, t)
actual := marshalStruct(pk, t)
expected := []byte{
175,
76,
33,
103,
184,
172,
12,
111,
24,
87,
84,
61,
243,
82,
99,
76,
131,
95,
171,
237,
145,
143,
7,
93,
205,
148,
104,
29,
153,
103,
187,
206,
112,
223,
252,
198,
102,
41,
38,
244,
228,
223,
102,
16,
216,
152,
231,
250,
7,
111,
90,
98,
194,
244,
101,
251,
69,
130,
11,
209,
41,
210,
133,
105,
217,
179,
190,
1,
6,
155,
135,
2,
168,
249,
253,
41,
59,
87,
8,
49,
231,
198,
142,
30,
186,
44,
175,
17,
198,
63,
210,
176,
237,
171,
11,
127,
}
if !bytes.Equal(actual, expected) {
t.Errorf("Expected GetPublicKeyVt to pass but failed.")
}
}
func testSignG1(message []byte, t *testing.T) {
sk := genSecretKey(t)
sig := genSignatureVt(sk, message, t)
pk := genPublicKeyVt(sk, t)
bls := NewSigPopVt()
if res, _ := bls.Verify(pk, message, sig); !res {
t.Errorf("createSignatureVt failed when it should've passed.")
}
}
func TestSignG1EmptyNilMessage(t *testing.T) {
sk := genSecretKey(t)
bls := NewSigPopVt()
sig, _ := bls.Sign(sk, nil)
pk := genPublicKeyVt(sk, t)
if res, _ := bls.Verify(pk, nil, sig); res {
t.Errorf("createSignature succeeded when it should've failed")
}
message := []byte{}
sig = genSignatureVt(sk, message, t)
if res, err := bls.Verify(pk, message, sig); !res {
t.Errorf("create and verify failed on empty message: %v", err)
}
}
func TestSignG1OneByteMessage(t *testing.T) {
message := []byte{1}
testSignG1(message, t)
}
func TestSignG1LargeMessage(t *testing.T) {
message := make([]byte, 1048576)
testSignG1(message, t)
}
func TestSignG1RandomMessage(t *testing.T) {
message := make([]byte, 65537)
readRand(message, t)
testSignG1(message, t)
}
func TestSignG1BadMessage(t *testing.T) {
message := make([]byte, 1024)
sk := genSecretKey(t)
sig := genSignatureVt(sk, message, t)
pk := genPublicKeyVt(sk, t)
message = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
bls := NewSigPopVt()
if res, _ := bls.Verify(pk, message, sig); res {
t.Errorf("Expected signature to not verify")
}
}
func TestBadConversionsG1(t *testing.T) {
sk := genSecretKey(t)
message := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
sig := genSignatureVt(sk, message, t)
pk := genPublicKeyVt(sk, t)
bls := NewSigPopVt()
if res, _ := bls.Verify(pk, message, sig); !res {
t.Errorf("Signature should be valid")
}
if res, _ := sig.verify(pk, message, blsSignaturePopVtDst); !res {
t.Errorf("Signature should be valid")
}
// Convert public key to signature in G2
sig2 := new(Signature)
err := sig2.UnmarshalBinary(marshalStruct(pk, t))
if err != nil {
t.Errorf("Should be able to convert to signature in G2")
}
pk2 := new(PublicKey)
err = pk2.UnmarshalBinary(marshalStruct(sig, t))
if err != nil {
t.Errorf("Should be able to convert to public key in G1")
}
res, _ := pk2.verifySignature(message, sig2, blsSignaturePopVtDst)
if res {
t.Errorf("The signature shouldn't verify")
}
}
func TestAggregatePublicKeysG2(t *testing.T) {
pks := []*PublicKeyVt{}
ikm := make([]byte, 32)
for i := 0; i < 20; i++ {
readRand(ikm, t)
sk := genRandSecretKey(ikm, t)
pk := genPublicKeyVt(sk, t)
pks = append(pks, pk)
}
apk1, err := aggregatePublicKeysVt(pks...)
if err != nil {
t.Errorf("%v", err)
}
rng := rand.New(rand.NewSource(1234567890))
rng.Shuffle(len(pks), func(i, j int) { pks[i], pks[j] = pks[j], pks[i] })
apk2, err := aggregatePublicKeysVt(pks...)
if err != nil {
t.Errorf("%v", err)
}
if !bytes.Equal(marshalStruct(apk1, t), marshalStruct(apk2, t)) {
t.Errorf("Aggregated public keys should be equal")
}
rand.Shuffle(len(pks), func(i, j int) { pks[i], pks[j] = pks[j], pks[i] })
apk1, err = aggregatePublicKeysVt(pks...)
if err != nil {
t.Errorf("%v", err)
}
if !bytes.Equal(marshalStruct(apk1, t), marshalStruct(apk2, t)) {
t.Errorf("Aggregated public keys should be equal")
}
}
func TestAggregateSignaturesG1(t *testing.T) {
sigs := []*SignatureVt{}
ikm := make([]byte, 32)
for i := 0; i < 20; i++ {
readRand(ikm, t)
sk := genRandSecretKey(ikm, t)
sig := genSignatureVt(sk, ikm, t)
sigs = append(sigs, sig)
}
asig1, err := aggregateSignaturesVt(sigs...)
if err != nil {
t.Errorf("%v", err)
}
rng2 := rand.New(rand.NewSource(1234567890))
rng2.Shuffle(len(sigs), func(i, j int) { sigs[i], sigs[j] = sigs[j], sigs[i] })
asig2, err := aggregateSignaturesVt(sigs...)
if err != nil {
t.Errorf("%v", err)
}
if !bytes.Equal(marshalStruct(asig1, t), marshalStruct(asig2, t)) {
t.Errorf("Aggregated signatures should be equal")
}
rand.Shuffle(len(sigs), func(i, j int) { sigs[i], sigs[j] = sigs[j], sigs[i] })
asig1, err = aggregateSignaturesVt(sigs...)
if err != nil {
t.Errorf("%v", err)
}
if !bytes.Equal(marshalStruct(asig1, t), marshalStruct(asig2, t)) {
t.Errorf("Aggregated signatures should be equal")
}
}
func initAggregatedTestValuesG1(messages [][]byte, t *testing.T) ([]*PublicKeyVt, []*SignatureVt) {
pks := []*PublicKeyVt{}
sigs := []*SignatureVt{}
ikm := make([]byte, 32)
for i := 0; i < numAggregateG1; i++ {
readRand(ikm, t)
sk := genRandSecretKey(ikm, t)
sig := genSignatureVt(sk, messages[i%len(messages)], t)
sigs = append(sigs, sig)
pk := genPublicKeyVt(sk, t)
pks = append(pks, pk)
}
return pks, sigs
}
func TestAggregatedFunctionalityG1(t *testing.T) {
message := make([]byte, 20)
messages := make([][]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
asig, err := bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
apk, err := bls.AggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
if res, _ := bls.VerifyMultiSignature(apk, message, asig); !res {
t.Errorf("Should verify aggregated signatures with same message")
}
if res, _ := asig.verify(apk, message, blsSignaturePopVtDst); !res {
t.Errorf("MultiSignature.verify failed.")
}
if res, _ := apk.verify(message, asig, blsSignaturePopVtDst); !res {
t.Errorf("MultiPublicKey.verify failed.")
}
}
func TestBadAggregatedFunctionalityG1(t *testing.T) {
message := make([]byte, 20)
messages := make([][]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
apk, err := bls.AggregatePublicKeys(pks[2:]...)
if err != nil {
t.Errorf("%v", err)
}
asig, err := bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
if res, _ := bls.VerifyMultiSignature(apk, message, asig); res {
t.Errorf(
"Should not verify aggregated signatures with same message when some public keys are missing",
)
}
apk, err = bls.AggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
asig, err = bls.AggregateSignatures(sigs[2:]...)
if err != nil {
t.Errorf("%v", err)
}
if res, _ := bls.VerifyMultiSignature(apk, message, asig); res {
t.Errorf(
"Should not verify aggregated signatures with same message when some signatures are missing",
)
}
asig, err = bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
badmsg := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
if res, _ := bls.VerifyMultiSignature(apk, badmsg, asig); res {
t.Errorf("Should not verify aggregated signature with bad message")
}
}
func TestAggregateVerifyG1Pass(t *testing.T) {
messages := make([][]byte, numAggregateG1)
for i := 0; i < numAggregateG1; i++ {
message := make([]byte, 20)
readRand(message, t)
messages[i] = message
}
pks, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
if res, _ := bls.AggregateVerify(pks, messages, sigs); !res {
t.Errorf("Expected aggregateVerify to pass but failed")
}
}
func TestAggregateVerifyG1MsgSigCntMismatch(t *testing.T) {
messages := make([][]byte, 8)
for i := 0; i < 8; i++ {
message := make([]byte, 20)
readRand(message, t)
messages[i] = message
}
pks, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
if res, _ := bls.AggregateVerify(pks, messages, sigs); res {
t.Errorf("Expected AggregateVerifyG1 to fail with duplicate message but passed")
}
}
func TestAggregateVerifyG1FailDupMsg(t *testing.T) {
messages := make([][]byte, 10)
for i := 0; i < 9; i++ {
message := make([]byte, 20)
readRand(message, t)
messages[i] = message
}
// Duplicate message
messages[9] = messages[0]
pks, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
if res, _ := bls.AggregateVerify(pks, messages, sigs); res {
t.Errorf("Expected aggregateVerify to fail with duplicate message but passed")
}
}
func TestAggregateVerifyG1FailIncorrectMsg(t *testing.T) {
messages := make([][]byte, 10)
for i := 0; i < 9; i++ {
message := make([]byte, 20)
readRand(message, t)
messages[i] = message
}
// Duplicate message
messages[9] = messages[0]
pks, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
if res, _ := bls.AggregateVerify(pks[2:], messages[2:], sigs); res {
t.Errorf("Expected aggregateVerify to fail with duplicate message but passed")
}
}
func TestAggregateVerifyG1OneMsg(t *testing.T) {
messages := make([][]byte, 1)
messages[0] = make([]byte, 20)
sk := genSecretKey(t)
sig := genSignatureVt(sk, messages[0], t)
pk := genPublicKeyVt(sk, t)
bls := NewSigPopVt()
// Should be the same as verifySignatureVt
if res, _ := bls.AggregateVerify([]*PublicKeyVt{pk}, messages, []*SignatureVt{sig}); !res {
t.Errorf("Expected AggregateVerifyG1OneMsg to pass but failed")
}
}
func TestVerifyG1Mutability(t *testing.T) {
// verify should not change any inputs
ikm := make([]byte, 32)
ikm_copy := make([]byte, 32)
readRand(ikm, t)
copy(ikm_copy, ikm)
bls := NewSigPopVt()
pk, sk, err := bls.KeygenWithSeed(ikm)
if !bytes.Equal(ikm, ikm_copy) {
t.Errorf("SigPopVt.KeygenWithSeed modifies ikm")
}
if err != nil {
t.Errorf("Expected KeygenWithSeed to succeed but failed.")
}
sig, err := bls.Sign(sk, ikm)
if !bytes.Equal(ikm, ikm_copy) {
t.Errorf("SigPopVt.Sign modifies message")
}
if err != nil {
t.Errorf("SigPopVt.KeygenWithSeed to succeed but failed.")
}
sigCopy := marshalStruct(sig, t)
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Expected verify to succeed but failed.")
}
if !bytes.Equal(ikm, ikm_copy) {
t.Errorf("SigPopVt.verify modifies message")
}
if !bytes.Equal(sigCopy, marshalStruct(sig, t)) {
t.Errorf("SigPopVt.verify modifies signature")
}
}
func TestPublicKeyG2FromBadBytes(t *testing.T) {
pk := make([]byte, 32)
err := new(PublicKeyVt).UnmarshalBinary(pk)
if err == nil {
t.Errorf("Expected PublicKeyG2FromBytes to fail but passed")
}
// All zeros
pk = make([]byte, PublicKeyVtSize)
// See https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// 1 << 7 == compressed
// 1 << 6 == infinity or zero
pk[0] = 0xc0
err = new(PublicKeyVt).UnmarshalBinary(pk)
if err == nil {
t.Errorf("Expected PublicKeyG2FromBytes to fail but passed")
}
sk := genSecretKey(t)
pk1, err := sk.GetPublicKeyVt()
if err != nil {
t.Errorf("Expected GetPublicKeyVt to pass but failed.")
}
out := marshalStruct(pk1, t)
out[3] += 1
err = new(PublicKeyVt).UnmarshalBinary(pk)
if err == nil {
t.Errorf("Expected PublicKeyG2FromBytes to fail but passed")
}
}
func TestSignatureG1FromBadBytes(t *testing.T) {
sig := make([]byte, 32)
err := new(SignatureVt).UnmarshalBinary(sig)
if err == nil {
t.Errorf("Expected SignatureG1FromBytes to fail but passed")
}
// All zeros
sig = make([]byte, SignatureVtSize)
// See https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// 1 << 7 == compressed
// 1 << 6 == infinity or zero
sig[0] = 0xc0
err = new(SignatureVt).UnmarshalBinary(sig)
if err == nil {
t.Errorf("Expected SignatureG1FromBytes to fail but passed")
}
}
func TestBadSecretKeyG1(t *testing.T) {
sk := &SecretKey{value: bls12381.Bls12381FqNew()}
pk, err := sk.GetPublicKeyVt()
if err == nil {
t.Errorf("Expected GetPublicKeyVt to fail with 0 byte secret key but passed: %v", pk)
}
_ = sk.UnmarshalBinary(sk.value.Params.BiModulus.Bytes())
pk, err = sk.GetPublicKeyVt()
if err == nil {
t.Errorf("Expected GetPublicKeyVt to fail with secret key with Q but passed: %v", pk)
}
err = sk.UnmarshalBinary([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})
if err == nil {
t.Errorf("Expected SecretKeyFromBytes to fail with not enough bytes but passed: %v", pk)
}
err = sk.UnmarshalBinary(make([]byte, 32))
if err == nil {
t.Errorf("Expected SecretKeyFromBytes to fail but passed: %v", pk)
}
}
func TestProofOfPossessionG1Works(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigPopVt()
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Key gen failed but should've succeeded")
}
pop, err := bls.PopProve(sk)
if err != nil {
t.Errorf("PopProve failed but should've succeeded")
}
if res, _ := bls.PopVerify(pk, pop); !res {
t.Errorf("PopVerify failed but should've succeeded")
}
}
func TestProofOfPossessionG1FromBadKey(t *testing.T) {
ikm := make([]byte, 32)
value := new(big.Int)
value.SetBytes(ikm)
sk := SecretKey{value: bls12381.Bls12381FqNew().SetBigInt(value)}
_, err := sk.createProofOfPossessionVt(blsSignaturePopVtDst)
if err == nil {
t.Errorf("createProofOfPossessionVt should've failed but succeeded.")
}
}
func TestProofOfPossessionG1BytesWorks(t *testing.T) {
sk := genSecretKey(t)
pop, err := sk.createProofOfPossessionVt(blsSignaturePopVtDst)
if err != nil {
t.Errorf("CreateProofOfPossesionG1 failed but shouldn've succeeded.")
}
out := marshalStruct(pop, t)
if len(out) != ProofOfPossessionVtSize {
t.Errorf(
"ProofOfPossessionBytes incorrect size: expected %v, got %v",
ProofOfPossessionVtSize,
len(out),
)
}
pop2 := new(ProofOfPossessionVt)
err = pop2.UnmarshalBinary(out)
if err != nil {
t.Errorf("ProofOfPossessionVt.UnmarshalBinary failed: %v", err)
}
out2 := marshalStruct(pop2, t)
if !bytes.Equal(out, out2) {
t.Errorf("ProofOfPossessionVt.UnmarshalBinary failed, not equal when deserialized")
}
}
func TestProofOfPossessionG1BadBytes(t *testing.T) {
zeros := make([]byte, ProofOfPossessionVtSize)
temp := new(ProofOfPossessionVt)
err := temp.UnmarshalBinary(zeros)
if err == nil {
t.Errorf("ProofOfPossessionVt.UnmarshalBinary shouldn've failed but succeeded.")
}
}
func TestProofOfPossessionG1Fails(t *testing.T) {
sk := genSecretKey(t)
pop, err := sk.createProofOfPossessionVt(blsSignaturePopVtDst)
if err != nil {
t.Errorf("Expected createProofOfPossessionVt to succeed but failed.")
}
ikm := make([]byte, 32)
readRand(ikm, t)
sk = genRandSecretKey(ikm, t)
bad, err := sk.GetPublicKeyVt()
if err != nil {
t.Errorf("Expected PublicKeyG2FromBytes to succeed but failed: %v", err)
}
if res, _ := pop.verify(bad, blsSignaturePopVtDst); res {
t.Errorf("Expected ProofOfPossession verify to fail but succeeded.")
}
}
func TestMultiSigG1Bytes(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 20)
messages[0] = message
_, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
msig, err := bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
msigBytes := marshalStruct(msig, t)
if len(msigBytes) != SignatureVtSize {
t.Errorf(
"Invalid multi-sig length. Expected %d bytes, found %d",
SignatureVtSize,
len(msigBytes),
)
}
msig2 := new(MultiSignatureVt)
err = msig2.UnmarshalBinary(msigBytes)
if err != nil {
t.Errorf("MultiSignatureG1FromBytes failed with %v", err)
}
msigBytes2 := marshalStruct(msig2, t)
if !bytes.Equal(msigBytes, msigBytes2) {
t.Errorf("Bytes methods not equal.")
}
}
func TestMultiSigG1BadBytes(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 20)
messages[0] = message
_, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
msig, err := bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
msigBytes := marshalStruct(msig, t)
if len(msigBytes) != SignatureVtSize {
t.Errorf(
"Invalid multi-sig length. Expected %d bytes, found %d",
SignatureSize,
len(msigBytes),
)
}
msigBytes[0] = 0
temp := new(MultiSignatureVt)
err = temp.UnmarshalBinary(msigBytes)
if err == nil {
t.Errorf("MultiSignatureG1FromBytes should've failed but succeeded")
}
msigBytes = make([]byte, SignatureVtSize)
err = temp.UnmarshalBinary(msigBytes)
if err == nil {
t.Errorf("MultiSignatureG1FromBytes should've failed but succeeded")
}
}
func TestMultiPubkeyG2Bytes(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 20)
messages[0] = message
pks, _ := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
apk, err := bls.AggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
apkBytes := marshalStruct(apk, t)
if len(apkBytes) != PublicKeyVtSize {
t.Errorf("MultiPublicKeyVt has an incorrect size")
}
apk2 := new(MultiPublicKeyVt)
err = apk2.UnmarshalBinary(apkBytes)
if err != nil {
t.Errorf("MultiPublicKeyVt.UnmarshalBinary failed with %v", err)
}
apk2Bytes := marshalStruct(apk2, t)
if !bytes.Equal(apkBytes, apk2Bytes) {
t.Errorf("Bytes methods not equal.")
}
}
func TestMultiPubkeyG2BadBytes(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 20)
messages[0] = message
pks, _ := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
apk, err := bls.AggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
apkBytes := marshalStruct(apk, t)
if len(apkBytes) != PublicKeyVtSize {
t.Errorf("MultiPublicKeyVt has an incorrect size")
}
apkBytes[0] = 0
temp := new(MultiPublicKeyVt)
err = temp.UnmarshalBinary(apkBytes)
if err == nil {
t.Errorf("MultiPublicKeyVt.UnmarshalBinary should've failed but succeeded")
}
apkBytes = make([]byte, PublicKeyVtSize)
err = temp.UnmarshalBinary(apkBytes)
if err == nil {
t.Errorf("MultiPublicKeyVt.UnmarshalBinary should've failed but succeeded")
}
}
func TestFastAggregateVerifyConstituentG1Works(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
if res, _ := bls.FastAggregateVerifyConstituent(pks, message, sigs); !res {
t.Errorf("FastAggregateVerify failed.")
}
}
func TestFastAggregateVerifyG1Works(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG1(messages, t)
asig, _ := aggregateSignaturesVt(sigs...)
bls := NewSigPopVt()
if res, _ := bls.FastAggregateVerify(pks, message, asig); !res {
t.Errorf("FastAggregateVerify failed.")
}
}
func TestFastAggregateVerifyG1Fails(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG1(messages, t)
bls := NewSigPopVt()
message[0] = 1
if res, _ := bls.FastAggregateVerifyConstituent(pks, message, sigs); res {
t.Errorf("FastAggregateVerify verified when it should've failed.")
}
}
func TestCustomPopDstG1Works(t *testing.T) {
bls, _ := NewSigPopVtWithDst("BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_POP_TEST",
"BLS_POP_BLS12381G1_XMD:SHA-256_SSWU_RO_POP_TEST")
msg := make([]byte, 20)
ikm := make([]byte, 32)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Couldn't create custom dst keys: %v", err)
}
sig, err := bls.Sign(sk, msg)
if err != nil {
t.Errorf("Couldn't sign with custom dst: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("verify fails with custom dst")
}
pks := make([]*PublicKeyVt, 10)
sigs := make([]*SignatureVt, 10)
pks[0] = pk
sigs[0] = sig
for i := 1; i < 10; i++ {
readRand(ikm, t)
pkt, skt, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Couldn't create custom dst keys: %v", err)
}
sigt, err := bls.Sign(skt, msg)
if err != nil {
t.Errorf("Couldn't sign with custom dst: %v", err)
}
pks[i] = pkt
sigs[i] = sigt
}
if res, _ := bls.FastAggregateVerifyConstituent(pks, msg, sigs); !res {
t.Errorf("FastAggregateVerify failed with custom dst")
}
pop, err := bls.PopProve(sk)
if err != nil {
t.Errorf("PopProve failed with custom dst")
}
if res, _ := bls.PopVerify(pk, pop); !res {
t.Errorf("PopVerify failed with custom dst")
}
}
func TestBlsPopG1KeyGenWithSeed(t *testing.T) {
ikm := []byte("Not enough bytes")
bls := NewSigPopVt()
_, _, err := bls.KeygenWithSeed(ikm)
if err == nil {
t.Errorf("Expected KeygenWithSeed to fail but succeeded")
}
}
func TestBlsPopG1KeyGen(t *testing.T) {
bls := NewSigPopVt()
_, _, err := bls.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
}
func TestPopVtThresholdKeygenBadInputs(t *testing.T) {
bls := NewSigPopVt()
_, _, err := bls.ThresholdKeygen(0, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(1, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(3, 2)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
}
func TestPopVtThresholdKeygen(t *testing.T) {
bls := NewSigPopVt()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
if len(sks) != 5 {
t.Errorf("ThresholdKeygen did not produce enough shares")
}
}
func TestPopPartialSignVt(t *testing.T) {
ikm := make([]byte, 32)
bls := NewSigPopVt()
pk, sks, err := bls.ThresholdKeygenWithSeed(ikm, 2, 4)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
msg := make([]byte, 10)
sig1, err := bls.PartialSign(sks[0], msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig2, err := bls.PartialSign(sks[1], msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig, err := bls.CombineSignatures(sig1, sig2)
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("Combined signature does not verify")
}
sig, err = bls.CombineSignatures(sig1)
if err == nil {
t.Errorf("CombineSignatures succeeded when it should've failed")
}
if res, _ := bls.Verify(pk, msg, sig); res {
t.Errorf("Combined signature verify succeeded when it should've failed")
}
}
// Ensure that mixed partial signatures from distinct origins create invalid composite signatures
func TestPopVtPartialMixupShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigPopVt()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
for i := range ikm {
ikm[i] = 1
}
pk2, sks2, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignatureVt, total)
sigs2 := make([]*PartialSignatureVt, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
sigs2[i], err = bls.PartialSign(sks2[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining 2 from group 1 and 2 from group 2
sig, err := bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[2], sigs2[3])
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
if res, _ := bls.Verify(pk2, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
// Should error out due to duplicate identifiers
_, err = bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[0], sigs2[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded.")
}
}
+478
View File
@@ -0,0 +1,478 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"fmt"
)
const (
// Domain separation tag for basic signatures
// according to section 4.2.1 in
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
blsSignatureBasicDst = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"
// Domain separation tag for basic signatures
// according to section 4.2.2 in
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
blsSignatureAugDst = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_"
// Domain separation tag for proof of possession signatures
// according to section 4.2.3 in
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
blsSignaturePopDst = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"
// Domain separation tag for proof of possession proofs
// according to section 4.2.3 in
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
blsPopProofDst = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"
)
type BlsScheme interface {
Keygen() (*PublicKey, *SecretKey, error)
KeygenWithSeed(ikm []byte) (*PublicKey, *SecretKey, error)
Sign(sk *SecretKey, msg []byte) (*Signature, error)
Verify(pk *PublicKey, msg []byte, sig *Signature) bool
AggregateVerify(pks []*PublicKey, msgs [][]byte, sigs []*Signature) bool
}
// generateKeys creates 32 bytes of random data to be fed to
// generateKeysWithSeed
func generateKeys() (*PublicKey, *SecretKey, error) {
ikm, err := generateRandBytes(32)
if err != nil {
return nil, nil, err
}
return generateKeysWithSeed(ikm)
}
// generateKeysWithSeed generates a BLS key pair given input key material (ikm)
func generateKeysWithSeed(ikm []byte) (*PublicKey, *SecretKey, error) {
sk, err := new(SecretKey).Generate(ikm)
if err != nil {
return nil, nil, err
}
pk, err := sk.GetPublicKey()
if err != nil {
return nil, nil, err
}
return pk, sk, nil
}
// thresholdGenerateKeys will generate random secret key shares and the corresponding public key
func thresholdGenerateKeys(threshold, total uint) (*PublicKey, []*SecretKeyShare, error) {
pk, sk, err := generateKeys()
if err != nil {
return nil, nil, err
}
shares, err := thresholdizeSecretKey(sk, threshold, total)
if err != nil {
return nil, nil, err
}
return pk, shares, nil
}
// thresholdGenerateKeysWithSeed will generate random secret key shares and the corresponding public key
// using the corresponding seed `ikm`
func thresholdGenerateKeysWithSeed(
ikm []byte,
threshold, total uint,
) (*PublicKey, []*SecretKeyShare, error) {
pk, sk, err := generateKeysWithSeed(ikm)
if err != nil {
return nil, nil, err
}
shares, err := thresholdizeSecretKey(sk, threshold, total)
if err != nil {
return nil, nil, err
}
return pk, shares, nil
}
// SigBasic is minimal-pubkey-size scheme that doesn't support FastAggregateVerificiation.
// see: https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03#section-4.2.1
type SigBasic struct {
dst string
}
// Creates a new BLS basic signature scheme with the standard domain separation tag used for signatures.
func NewSigBasic() *SigBasic {
return &SigBasic{dst: blsSignatureBasicDst}
}
// Creates a new BLS basic signature scheme with a custom domain separation tag used for signatures.
func NewSigBasicWithDst(signDst string) *SigBasic {
return &SigBasic{dst: signDst}
}
// Creates a new BLS key pair
func (b SigBasic) Keygen() (*PublicKey, *SecretKey, error) {
return generateKeys()
}
// Creates a new BLS key pair
// Input key material (ikm) MUST be at least 32 bytes long,
// but it MAY be longer.
func (b SigBasic) KeygenWithSeed(ikm []byte) (*PublicKey, *SecretKey, error) {
return generateKeysWithSeed(ikm)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigBasic) ThresholdKeygen(threshold, total uint) (*PublicKey, []*SecretKeyShare, error) {
return thresholdGenerateKeys(threshold, total)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigBasic) ThresholdKeygenWithSeed(
ikm []byte,
threshold, total uint,
) (*PublicKey, []*SecretKeyShare, error) {
return thresholdGenerateKeysWithSeed(ikm, threshold, total)
}
// Computes a signature in G2 from sk, a secret key, and a message
func (b SigBasic) Sign(sk *SecretKey, msg []byte) (*Signature, error) {
return sk.createSignature(msg, b.dst)
}
// Compute a partial signature in G2 that can be combined with other partial signature
func (b SigBasic) PartialSign(sks *SecretKeyShare, msg []byte) (*PartialSignature, error) {
return sks.partialSign(msg, b.dst)
}
// CombineSignatures takes partial signatures to yield a completed signature
func (b SigBasic) CombineSignatures(sigs ...*PartialSignature) (*Signature, error) {
return combineSigs(sigs)
}
// Checks that a signature is valid for the message under the public key pk
func (b SigBasic) Verify(pk *PublicKey, msg []byte, sig *Signature) (bool, error) {
return pk.verifySignature(msg, sig, b.dst)
}
// The AggregateVerify algorithm checks an aggregated signature over
// several (PK, message, signature) pairs.
// Each message must be different or this will return false.
// See section 3.1.1 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigBasic) AggregateVerify(
pks []*PublicKey,
msgs [][]byte,
sigs []*Signature,
) (bool, error) {
if !allRowsUnique(msgs) {
return false, fmt.Errorf("all messages must be distinct")
}
asig, err := aggregateSignatures(sigs...)
if err != nil {
return false, err
}
return asig.aggregateVerify(pks, msgs, b.dst)
}
// SigAug is minimal-pubkey-size scheme that doesn't support FastAggregateVerificiation.
// see: https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03#section-4.2.2
type SigAug struct {
dst string
}
// Creates a new BLS message augmentation signature scheme with the standard domain separation tag used for signatures.
func NewSigAug() *SigAug {
return &SigAug{dst: blsSignatureAugDst}
}
// Creates a new BLS message augmentation signature scheme with a custom domain separation tag used for signatures.
func NewSigAugWithDst(signDst string) *SigAug {
return &SigAug{dst: signDst}
}
// Creates a new BLS key pair
func (b SigAug) Keygen() (*PublicKey, *SecretKey, error) {
return generateKeys()
}
// Creates a new BLS secret key
// Input key material (ikm) MUST be at least 32 bytes long,
// but it MAY be longer.
func (b SigAug) KeygenWithSeed(ikm []byte) (*PublicKey, *SecretKey, error) {
return generateKeysWithSeed(ikm)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigAug) ThresholdKeygen(threshold, total uint) (*PublicKey, []*SecretKeyShare, error) {
return thresholdGenerateKeys(threshold, total)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigAug) ThresholdKeygenWithSeed(
ikm []byte,
threshold, total uint,
) (*PublicKey, []*SecretKeyShare, error) {
return thresholdGenerateKeysWithSeed(ikm, threshold, total)
}
// Computes a signature in G1 from sk, a secret key, and a message
// See section 3.2.1 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigAug) Sign(sk *SecretKey, msg []byte) (*Signature, error) {
if len(msg) == 0 {
return nil, fmt.Errorf("message cannot be empty or nil")
}
pk, err := sk.GetPublicKey()
if err != nil {
return nil, err
}
bytes, err := pk.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("MarshalBinary failed")
}
bytes = append(bytes, msg...)
return sk.createSignature(bytes, b.dst)
}
// Compute a partial signature in G2 that can be combined with other partial signature
func (b SigAug) PartialSign(
sks *SecretKeyShare,
pk *PublicKey,
msg []byte,
) (*PartialSignature, error) {
if len(msg) == 0 {
return nil, fmt.Errorf("message cannot be empty or nil")
}
bytes, err := pk.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("MarshalBinary failed")
}
bytes = append(bytes, msg...)
return sks.partialSign(bytes, b.dst)
}
// CombineSignatures takes partial signatures to yield a completed signature
func (b SigAug) CombineSignatures(sigs ...*PartialSignature) (*Signature, error) {
return combineSigs(sigs)
}
// Checks that a signature is valid for the message under the public key pk
// See section 3.2.2 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigAug) Verify(pk *PublicKey, msg []byte, sig *Signature) (bool, error) {
bytes, err := pk.MarshalBinary()
if err != nil {
return false, err
}
bytes = append(bytes, msg...)
return pk.verifySignature(bytes, sig, b.dst)
}
// The AggregateVerify algorithm checks an aggregated signature over
// several (PK, message, signature) pairs.
// See section 3.2.3 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigAug) AggregateVerify(pks []*PublicKey, msgs [][]byte, sigs []*Signature) (bool, error) {
if len(pks) != len(msgs) {
return false, fmt.Errorf(
"the number of public keys does not match the number of messages: %v != %v",
len(pks),
len(msgs),
)
}
data := make([][]byte, len(msgs))
for i, msg := range msgs {
bytes, err := pks[i].MarshalBinary()
if err != nil {
return false, err
}
data[i] = append(bytes, msg...)
}
asig, err := aggregateSignatures(sigs...)
if err != nil {
return false, err
}
return asig.aggregateVerify(pks, data, b.dst)
}
// SigEth2 supports signatures on Eth2.
// Internally is an alias for SigPop
type SigEth2 = SigPop
// NewSigEth2 Creates a new BLS ETH2 signature scheme with the standard domain separation tag used for signatures.
func NewSigEth2() *SigEth2 {
return NewSigPop()
}
// SigPop is minimal-pubkey-size scheme that supports FastAggregateVerification
// and requires using proofs of possession to mitigate rogue-key attacks
// see: https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03#section-4.2.3
type SigPop struct {
sigDst string
popDst string
}
// Creates a new BLS proof of possession signature scheme with the standard domain separation tag used for signatures.
func NewSigPop() *SigPop {
return &SigPop{sigDst: blsSignaturePopDst, popDst: blsPopProofDst}
}
// Creates a new BLS message proof of possession signature scheme with a custom domain separation tag used for signatures.
func NewSigPopWithDst(signDst, popDst string) (*SigPop, error) {
if signDst == popDst {
return nil, fmt.Errorf("domain separation tags cannot be equal")
}
return &SigPop{sigDst: signDst, popDst: popDst}, nil
}
// Creates a new BLS key pair
func (b SigPop) Keygen() (*PublicKey, *SecretKey, error) {
return generateKeys()
}
// Creates a new BLS secret key
// Input key material (ikm) MUST be at least 32 bytes long,
// but it MAY be longer.
func (b SigPop) KeygenWithSeed(ikm []byte) (*PublicKey, *SecretKey, error) {
return generateKeysWithSeed(ikm)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigPop) ThresholdKeygen(threshold, total uint) (*PublicKey, []*SecretKeyShare, error) {
return thresholdGenerateKeys(threshold, total)
}
// ThresholdKeyGen generates a public key and `total` secret key shares such that
// `threshold` of them can be combined in signatures
func (b SigPop) ThresholdKeygenWithSeed(
ikm []byte,
threshold, total uint,
) (*PublicKey, []*SecretKeyShare, error) {
return thresholdGenerateKeysWithSeed(ikm, threshold, total)
}
// Computes a signature in G2 from sk, a secret key, and a message
// See section 2.6 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPop) Sign(sk *SecretKey, msg []byte) (*Signature, error) {
return sk.createSignature(msg, b.sigDst)
}
// Compute a partial signature in G2 that can be combined with other partial signature
func (b SigPop) PartialSign(sks *SecretKeyShare, msg []byte) (*PartialSignature, error) {
return sks.partialSign(msg, b.sigDst)
}
// CombineSignatures takes partial signatures to yield a completed signature
func (b SigPop) CombineSignatures(sigs ...*PartialSignature) (*Signature, error) {
return combineSigs(sigs)
}
// Checks that a signature is valid for the message under the public key pk
// See section 2.7 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPop) Verify(pk *PublicKey, msg []byte, sig *Signature) (bool, error) {
return pk.verifySignature(msg, sig, b.sigDst)
}
// The aggregateVerify algorithm checks an aggregated signature over
// several (PK, message, signature) pairs.
// Each message must be different or this will return false.
// See section 3.1.1 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPop) AggregateVerify(pks []*PublicKey, msgs [][]byte, sigs []*Signature) (bool, error) {
if !allRowsUnique(msgs) {
return false, fmt.Errorf("all messages must be distinct")
}
asig, err := aggregateSignatures(sigs...)
if err != nil {
return false, err
}
return asig.aggregateVerify(pks, msgs, b.sigDst)
}
// Combine many signatures together to form a Multisignature.
// Multisignatures can be created when multiple signers jointly
// generate signatures over the same message.
func (b SigPop) AggregateSignatures(sigs ...*Signature) (*MultiSignature, error) {
g1, err := aggregateSignatures(sigs...)
if err != nil {
return nil, err
}
return &MultiSignature{value: g1.Value}, nil
}
// Combine many public keys together to form a Multipublickey.
// Multipublickeys are used to verify multisignatures.
func (b SigPop) AggregatePublicKeys(pks ...*PublicKey) (*MultiPublicKey, error) {
g2, err := aggregatePublicKeys(pks...)
if err != nil {
return nil, err
}
return &MultiPublicKey{value: g2.value}, nil
}
// Checks that a multisignature is valid for the message under the multi public key
// Similar to FastAggregateVerify except the keys and signatures have already been
// combined. See section 3.3.4 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPop) VerifyMultiSignature(
pk *MultiPublicKey,
msg []byte,
sig *MultiSignature,
) (bool, error) {
s := &Signature{Value: sig.value}
p := &PublicKey{value: pk.value}
return p.verifySignature(msg, s, b.sigDst)
}
// FastAggregateVerify verifies an aggregated signature against the specified message and set of public keys.
// See section 3.3.4 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPop) FastAggregateVerify(pks []*PublicKey, msg []byte, asig *Signature) (bool, error) {
apk, err := aggregatePublicKeys(pks...)
if err != nil {
return false, err
}
return apk.verifySignature(msg, asig, b.sigDst)
}
// FastAggregateVerifyConstituent aggregates all constituent signatures and the verifies
// them against the specified message and public keys
// See section 3.3.4 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPop) FastAggregateVerifyConstituent(
pks []*PublicKey,
msg []byte,
sigs []*Signature,
) (bool, error) {
// Aggregate the constituent signatures
asig, err := aggregateSignatures(sigs...)
if err != nil {
return false, err
}
// And verify
return b.FastAggregateVerify(pks, msg, asig)
}
// Create a proof of possession for the corresponding public key.
// A proof of possession must be created for each public key to be used
// in FastAggregateVerify or a Multipublickey to avoid rogue key attacks.
// See section 3.3.2 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPop) PopProve(sk *SecretKey) (*ProofOfPossession, error) {
return sk.createProofOfPossession(b.popDst)
}
// verify a proof of possession for the corresponding public key is valid.
// A proof of possession must be created for each public key to be used
// in FastAggregateVerify or a Multipublickey to avoid rogue key attacks.
// See section 3.3.3 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (b SigPop) PopVerify(pk *PublicKey, pop2 *ProofOfPossession) (bool, error) {
return pop2.verify(pk, b.popDst)
}
+507
View File
@@ -0,0 +1,507 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"fmt"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
"github.com/sonr-io/sonr/crypto/internal"
)
// Implement BLS signatures on the BLS12-381 curve
// according to https://crypto.standford.edu/~dabo/pubs/papers/BLSmultisig.html
// and https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
// this file implements signatures in G2 and public keys in G1.
// Public Keys and Signatures can be aggregated but the consumer
// must use proofs of possession to defend against rogue-key attacks.
const (
// Public key size in G1
PublicKeySize = 48
// Signature size in G2
SignatureSize = 96
// Proof of Possession in G2
ProofOfPossessionSize = 96
)
// Represents a public key in G1
type PublicKey struct {
value bls12381.G1
}
// Serialize a public key to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (pk PublicKey) MarshalBinary() ([]byte, error) {
out := pk.value.ToCompressed()
return out[:], nil
}
// Deserialize a public key from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the public key
// otherwise it will return an error
func (pk *PublicKey) UnmarshalBinary(data []byte) error {
if len(data) != PublicKeySize {
return fmt.Errorf("public key must be %d bytes", PublicKeySize)
}
var blob [PublicKeySize]byte
copy(blob[:], data)
p1, err := new(bls12381.G1).FromCompressed(&blob)
if err != nil {
return err
}
if p1.IsIdentity() == 1 {
return fmt.Errorf("public keys cannot be zero")
}
pk.value = *p1
return nil
}
// Represents a BLS signature in G2
type Signature struct {
Value bls12381.G2
}
// Serialize a signature to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (sig Signature) MarshalBinary() ([]byte, error) {
out := sig.Value.ToCompressed()
return out[:], nil
}
func (sig Signature) verify(pk *PublicKey, message []byte, signDst string) (bool, error) {
return pk.verifySignature(message, &sig, signDst)
}
// The AggregateVerify algorithm checks an aggregated signature over
// several (PK, message) pairs.
// The Signature is the output of aggregateSignatures
// Each message must be different or this will return false
// See section 3.1.1 from
// https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (sig Signature) aggregateVerify(
pks []*PublicKey,
msgs [][]byte,
signDst string,
) (bool, error) {
return sig.coreAggregateVerify(pks, msgs, signDst)
}
func (sig Signature) coreAggregateVerify(
pks []*PublicKey,
msgs [][]byte,
signDst string,
) (bool, error) {
if len(pks) < 1 {
return false, fmt.Errorf("at least one key is required")
}
if len(msgs) < 1 {
return false, fmt.Errorf("at least one message is required")
}
if len(pks) != len(msgs) {
return false, fmt.Errorf(
"the number of public keys does not match the number of messages: %v != %v",
len(pks),
len(msgs),
)
}
if sig.Value.InCorrectSubgroup() == 0 {
return false, fmt.Errorf("signature is not in the correct subgroup")
}
dst := []byte(signDst)
engine := new(bls12381.Engine)
// e(pk_1, H(m_1))*...*e(pk_N, H(m_N)) == e(g1, s)
// However, we use only one miller loop
// by doing the equivalent of
// e(pk_1, H(m_1))*...*e(pk_N, H(m_N)) * e(g1^-1, s) == 1
for i, pk := range pks {
if pk == nil {
return false, fmt.Errorf("public key at %d is nil", i)
}
if pk.value.IsIdentity() == 1 || pk.value.InCorrectSubgroup() == 0 {
return false, fmt.Errorf("public key at %d is not in the correct subgroup", i)
}
p2 := new(bls12381.G2).Hash(native.EllipticPointHasherSha256(), msgs[i], dst)
engine.AddPair(&pk.value, p2)
}
engine.AddPairInvG1(new(bls12381.G1).Generator(), &sig.Value)
return engine.Check(), nil
}
// Deserialize a signature from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the Signature
// otherwise it will return an error
func (sig *Signature) UnmarshalBinary(data []byte) error {
if len(data) != SignatureSize {
return fmt.Errorf("signature must be %d bytes", SignatureSize)
}
var blob [SignatureSize]byte
copy(blob[:], data)
p2, err := new(bls12381.G2).FromCompressed(&blob)
if err != nil {
return err
}
if p2.IsIdentity() == 1 {
return fmt.Errorf("signatures cannot be zero")
}
sig.Value = *p2
return nil
}
// Get the corresponding public key from a secret key
// Verifies the public key is in the correct subgroup
func (sk SecretKey) GetPublicKey() (*PublicKey, error) {
result := new(bls12381.G1).Generator()
result.Mul(result, sk.value)
if result.InCorrectSubgroup() == 0 || result.IsIdentity() == 1 {
return nil, fmt.Errorf("point is not in correct subgroup")
}
return &PublicKey{value: *result}, nil
}
// Compute a signature from a secret key and message
// This signature is deterministic which protects against
// attacks arising from signing with bad randomness like
// the nonce reuse attack on ECDSA. `message` is
// hashed to a point in G2 as described in to
// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/?include_text=1
// See Section 2.6 in https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
// nil message is not permitted but empty slice is allowed
func (sk SecretKey) createSignature(message []byte, dst string) (*Signature, error) {
if message == nil {
return nil, fmt.Errorf("message cannot be nil")
}
if sk.value.IsZero() == 1 {
return nil, fmt.Errorf("invalid secret key")
}
p2 := new(bls12381.G2).Hash(native.EllipticPointHasherSha256(), message, []byte(dst))
result := new(bls12381.G2).Mul(p2, sk.value)
if result.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("point is not on correct subgroup")
}
return &Signature{Value: *result}, nil
}
// Verify a signature is valid for the message under this public key.
// See Section 2.7 in https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
func (pk PublicKey) verifySignature(
message []byte,
signature *Signature,
dst string,
) (bool, error) {
if signature == nil || message == nil || pk.value.IsIdentity() == 1 {
return false, fmt.Errorf("signature and message and public key cannot be nil or zero")
}
if signature.Value.IsIdentity() == 1 || signature.Value.InCorrectSubgroup() == 0 {
return false, fmt.Errorf("signature is not in the correct subgroup")
}
engine := new(bls12381.Engine)
p2 := new(bls12381.G2).Hash(native.EllipticPointHasherSha256(), message, []byte(dst))
// e(pk, H(m)) == e(g1, s)
// However, we can reduce the number of miller loops
// by doing the equivalent of
// e(pk^-1, H(m)) * e(g1, s) == 1
engine.AddPair(&pk.value, p2)
engine.AddPairInvG1(new(bls12381.G1).Generator(), &signature.Value)
return engine.Check(), nil
}
// Combine public keys into one aggregated key
func aggregatePublicKeys(pks ...*PublicKey) (*PublicKey, error) {
if len(pks) < 1 {
return nil, fmt.Errorf("at least one public key is required")
}
result := new(bls12381.G1).Identity()
for i, k := range pks {
if k == nil {
return nil, fmt.Errorf("key at %d is nil, keys cannot be nil", i)
}
if k.value.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("key at %d is not in the correct subgroup", i)
}
result.Add(result, &k.value)
}
return &PublicKey{value: *result}, nil
}
// Combine signatures into one aggregated signature
func aggregateSignatures(sigs ...*Signature) (*Signature, error) {
if len(sigs) < 1 {
return nil, fmt.Errorf("at least one signature is required")
}
result := new(bls12381.G2).Identity()
for i, s := range sigs {
if s == nil {
return nil, fmt.Errorf("signature at %d is nil, signature cannot be nil", i)
}
if s.Value.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("signature at %d is not in the correct subgroup", i)
}
result.Add(result, &s.Value)
}
return &Signature{Value: *result}, nil
}
// A proof of possession scheme uses a separate public key validation
// step, called a proof of possession, to defend against rogue key
// attacks. This enables an optimization to aggregate signature
// verification for the case that all signatures are on the same
// message.
type ProofOfPossession struct {
value bls12381.G2
}
// Generates a proof-of-possession (PoP) for this secret key. The PoP signature should be verified before
// before accepting any aggregate signatures related to the corresponding pubkey.
func (sk SecretKey) createProofOfPossession(popDst string) (*ProofOfPossession, error) {
pk, err := sk.GetPublicKey()
if err != nil {
return nil, err
}
msg, err := pk.MarshalBinary()
if err != nil {
return nil, err
}
sig, err := sk.createSignature(msg, popDst)
if err != nil {
return nil, err
}
return &ProofOfPossession{value: sig.Value}, nil
}
// Serialize a proof of possession to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (pop ProofOfPossession) MarshalBinary() ([]byte, error) {
out := pop.value.ToCompressed()
return out[:], nil
}
// Deserialize a proof of possession from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the Signature
// otherwise it will return an error
func (pop *ProofOfPossession) UnmarshalBinary(data []byte) error {
p2 := new(Signature)
err := p2.UnmarshalBinary(data)
if err != nil {
return err
}
pop.value = p2.Value
return nil
}
// Verifies that PoP is valid for this pubkey. In order to prevent rogue key attacks, a PoP must be validated
// for each pubkey in an aggregated signature.
func (pop ProofOfPossession) verify(pk *PublicKey, popDst string) (bool, error) {
if pk == nil {
return false, fmt.Errorf("public key cannot be nil")
}
msg, err := pk.MarshalBinary()
if err != nil {
return false, err
}
return pk.verifySignature(msg, &Signature{Value: pop.value}, popDst)
}
// Represents an MultiSignature in G2. A multisignature is used when multiple signatures
// are calculated over the same message vs an aggregate signature where each message signed
// is a unique.
type MultiSignature struct {
value bls12381.G2
}
// Serialize a multi-signature to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (sig MultiSignature) MarshalBinary() ([]byte, error) {
out := sig.value.ToCompressed()
return out[:], nil
}
// Check a multisignature is valid for a multipublickey and a message
func (sig MultiSignature) verify(pk *MultiPublicKey, message []byte, signDst string) (bool, error) {
if pk == nil {
return false, fmt.Errorf("public key cannot be nil")
}
p := PublicKey{value: pk.value}
return p.verifySignature(message, &Signature{Value: sig.value}, signDst)
}
// Deserialize a signature from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the Signature
// otherwise it will return an error
func (sig *MultiSignature) UnmarshalBinary(data []byte) error {
if len(data) != SignatureSize {
return fmt.Errorf("multi signature must be %v bytes", SignatureSize)
}
s2 := new(Signature)
err := s2.UnmarshalBinary(data)
if err != nil {
return err
}
sig.value = s2.Value
return nil
}
// Represents accumulated multiple Public Keys in G1 for verifying a multisignature
type MultiPublicKey struct {
value bls12381.G1
}
// Serialize a public key to a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
func (pk MultiPublicKey) MarshalBinary() ([]byte, error) {
out := pk.value.ToCompressed()
return out[:], nil
}
// Deserialize a public key from a byte array in compressed form.
// See
// https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// https://docs.rs/bls12_381/0.1.1/bls12_381/notes/serialization/index.html
// If successful, it will assign the public key
// otherwise it will return an error
func (pk *MultiPublicKey) UnmarshalBinary(data []byte) error {
if len(data) != PublicKeySize {
return fmt.Errorf("multi public key must be %v bytes", PublicKeySize)
}
p1 := new(PublicKey)
err := p1.UnmarshalBinary(data)
if err != nil {
return err
}
pk.value = p1.value
return nil
}
// Check a multisignature is valid for a multipublickey and a message
func (pk MultiPublicKey) verify(message []byte, sig *MultiSignature, signDst string) (bool, error) {
return sig.verify(&pk, message, signDst)
}
// PartialSignature represents threshold Gap Diffie-Hellman BLS signature
// that can be combined with other partials to yield a completed BLS signature
// See section 3.2 in <https://www.cc.gatech.edu/~aboldyre/papers/bold.pdf>
type PartialSignature struct {
Identifier byte
Signature bls12381.G2
}
// partialSign creates a partial signature that can be combined with other partial signatures
// to yield a complete signature
func (sks SecretKeyShare) partialSign(message []byte, signDst string) (*PartialSignature, error) {
if len(message) == 0 {
return nil, fmt.Errorf("message cannot be empty or nil")
}
p2 := new(bls12381.G2).Hash(native.EllipticPointHasherSha256(), message, []byte(signDst))
var blob [SecretKeySize]byte
copy(blob[:], internal.ReverseScalarBytes(sks.value))
s, err := bls12381.Bls12381FqNew().SetBytes(&blob)
if err != nil {
return nil, err
}
result := new(bls12381.G2).Mul(p2, s)
if result.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("point is not on correct subgroup")
}
return &PartialSignature{Identifier: sks.identifier, Signature: *result}, nil
}
// combineSigs gathers partial signatures and yields a complete signature
func combineSigs(partials []*PartialSignature) (*Signature, error) {
if len(partials) < 2 {
return nil, fmt.Errorf("must have at least 2 partial signatures")
}
if len(partials) > 255 {
return nil, fmt.Errorf("unsupported to combine more than 255 signatures")
}
// Don't know the actual values so put the minimum
xVars, yVars, err := splitXY(partials)
if err != nil {
return nil, err
}
sTmp := new(bls12381.G2).Identity()
sig := new(bls12381.G2).Identity()
// Lagrange interpolation
basis := bls12381.Bls12381FqNew().SetOne()
for i, xi := range xVars {
basis.SetOne()
for j, xj := range xVars {
if i == j {
continue
}
num := bls12381.Bls12381FqNew().Neg(xj) // - x_m
den := bls12381.Bls12381FqNew().Sub(xi, xj) // x_j - x_m
_, wasInverted := den.Invert(den)
// wasInverted == false if den == 0
if !wasInverted {
return nil, fmt.Errorf("signatures cannot be recombined")
}
basis.Mul(basis, num.Mul(num, den))
}
sTmp.Mul(yVars[i], basis)
sig.Add(sig, sTmp)
}
if sig.InCorrectSubgroup() == 0 {
return nil, fmt.Errorf("signature is not in the correct subgroup")
}
return &Signature{Value: *sig}, nil
}
// Ensure no duplicates x values and convert x values to field elements
func splitXY(partials []*PartialSignature) ([]*native.Field, []*bls12381.G2, error) {
x := make([]*native.Field, len(partials))
y := make([]*bls12381.G2, len(partials))
dup := make(map[byte]bool)
for i, sp := range partials {
if sp == nil {
return nil, nil, fmt.Errorf("partial signature cannot be nil")
}
if _, exists := dup[sp.Identifier]; exists {
return nil, nil, fmt.Errorf("duplicate signature included")
}
if sp.Signature.InCorrectSubgroup() == 0 {
return nil, nil, fmt.Errorf("signature is not in the correct subgroup")
}
dup[sp.Identifier] = true
x[i] = bls12381.Bls12381FqNew().SetUint64(uint64(sp.Identifier))
y[i] = &sp.Signature
}
return x, y, nil
}
@@ -0,0 +1,417 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"testing"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
)
func generateAugSignatureG2(sk *SecretKey, msg []byte, t *testing.T) *Signature {
bls := NewSigAug()
sig, err := bls.Sign(sk, msg)
if err != nil {
t.Errorf("Aug Sign failed")
}
return sig
}
func generateAugAggregateDataG2(t *testing.T) ([]*PublicKey, []*Signature, [][]byte) {
msgs := make([][]byte, numAggregateG2)
pks := make([]*PublicKey, numAggregateG2)
sigs := make([]*Signature, numAggregateG2)
ikm := make([]byte, 32)
bls := NewSigAug()
for i := 0; i < numAggregateG2; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
msg := make([]byte, 20)
readRand(msg, t)
sig := generateAugSignatureG2(sk, msg, t)
msgs[i] = msg
sigs[i] = sig
pks[i] = pk
}
return pks, sigs, msgs
}
func TestAugKeyGenG2Works(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAug()
_, _, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
}
func TestAugKeyGenG2Fail(t *testing.T) {
ikm := make([]byte, 10)
readRand(ikm, t)
bls := NewSigAug()
_, _, err := bls.KeygenWithSeed(ikm)
if err == nil {
t.Errorf("Aug KeyGen succeeded when it should've failed")
}
}
func TestAugCustomDstG2(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAugWithDst("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_TEST")
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug Custom Dst KeyGen failed")
}
readRand(ikm, t)
sig, err := bls.Sign(sk, ikm)
if err != nil {
t.Errorf("Aug Custom Dst Sign failed")
}
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Aug Custon Dst Verify failed")
}
ikm[0] = 0
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Aug Custom Dst Verify succeeded when it should've failed.")
}
}
func TestAugSigningG2(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAug()
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
readRand(ikm, t)
sig := generateAugSignatureG2(sk, ikm, t)
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Aug Verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Aug Verify succeeded when it should've failed.")
}
}
func TestAugSignEmptyMessage(t *testing.T) {
bls := NewSigAug()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Aug KeyGen failed")
}
// Sign a nil message
_, err = bls.Sign(sk, nil)
if err == nil {
t.Errorf("Expected sign of nil message to fail")
}
}
func TestAugSignNilMessage(t *testing.T) {
bls := NewSigAug()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Aug KeyGen failed")
}
// Sign an empty message
_, err = bls.Sign(sk, []byte{})
if err == nil {
t.Errorf("Expected sign of empty message to fail")
}
}
func TestAugAggregateVerifyG2Works(t *testing.T) {
pks, sigs, msgs := generateAugAggregateDataG2(t)
bls := NewSigAug()
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug AggregateVerify failed")
}
}
func TestAugAggregateVerifyG2BadPks(t *testing.T) {
bls := NewSigAug()
pks, sigs, msgs := generateAugAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug AggregateVerify failed")
}
pks[0] = pks[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug AggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
pkValue := new(bls12381.G1).Identity()
pks[0] = &PublicKey{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug AggregateVerify succeeded with zero byte public key it should've failed")
}
// Try with base generator
pkValue.Generator()
pks[0] = &PublicKey{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf(
"Aug aggregateVerify succeeded with the base generator public key it should've failed",
)
}
}
func TestAugAggregateVerifyG2BadSigs(t *testing.T) {
bls := NewSigAug()
pks, sigs, msgs := generateAugAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug aggregateVerify failed")
}
sigs[0] = sigs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
sigValue := new(bls12381.G2).Identity()
sigs[0] = &Signature{Value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded with zero byte signature it should've failed")
}
// Try with base generator
sigValue.Generator()
sigs[0] = &Signature{Value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf(
"Aug aggregateVerify succeeded with the base generator signature it should've failed",
)
}
}
func TestAugAggregateVerifyG2BadMsgs(t *testing.T) {
bls := NewSigAug()
pks, sigs, msgs := generateAugAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug aggregateVerify failed")
}
// Test len(pks) != len(msgs)
if res, _ := bls.AggregateVerify(pks, msgs[0:8], sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
msgs[0] = msgs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
}
func TestAugAggregateVerifyG2DupMsg(t *testing.T) {
bls := NewSigAug()
// Only two messages but repeated
messages := make([][]byte, numAggregateG2)
messages[0] = []byte("Yes")
messages[1] = []byte("No")
for i := 2; i < numAggregateG2; i++ {
messages[i] = messages[i%2]
}
pks := make([]*PublicKey, numAggregateG2)
sigs := make([]*Signature, numAggregateG2)
ikm := make([]byte, 32)
for i := 0; i < numAggregateG2; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
sig := generateAugSignatureG2(sk, messages[i], t)
pks[i] = pk
sigs[i] = sig
}
if res, _ := bls.AggregateVerify(pks, messages, sigs); !res {
t.Errorf("Aug aggregateVerify failed for duplicate messages")
}
}
func TestBlsAugG2KeyGen(t *testing.T) {
bls := NewSigAug()
_, _, err := bls.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
}
func TestAugThresholdKeygenBadInputs(t *testing.T) {
bls := NewSigAug()
_, _, err := bls.ThresholdKeygen(0, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(1, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(3, 2)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
}
func TestAugThresholdKeygen(t *testing.T) {
bls := NewSigAug()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
if len(sks) != 5 {
t.Errorf("ThresholdKeygen did not produce enough shares")
}
}
func TestAugPartialSign(t *testing.T) {
ikm := make([]byte, 32)
bls := NewSigAug()
pk, sks, err := bls.ThresholdKeygenWithSeed(ikm, 2, 4)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
msg := make([]byte, 10)
sig1, err := bls.PartialSign(sks[0], pk, msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig2, err := bls.PartialSign(sks[1], pk, msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig, err := bls.CombineSignatures(sig1, sig2)
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("Combined signature does not verify")
}
sig, err = bls.CombineSignatures(sig1)
if err == nil {
t.Errorf("CombineSignatures succeeded when it should've failed")
}
if res, _ := bls.Verify(pk, msg, sig); res {
t.Errorf("Combined signature verify succeeded when it should've failed")
}
}
// Ensure that mixed partial signatures from distinct origins create invalid composite signatures
func TestAugPartialMixupShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigAug()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
for i := range ikm {
ikm[i] = 1
}
pk2, sks2, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignature, total)
sigs2 := make([]*PartialSignature, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], pk1, msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
sigs2[i], err = bls.PartialSign(sks2[i], pk2, msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining 2 from group 1 and 2 from group 2
sig, err := bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[2], sigs2[3])
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
if res, _ := bls.Verify(pk2, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
// Should error out due to duplicate identifiers
_, err = bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[0], sigs2[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded.")
}
}
func TestAugPartialSignEmptyMessage(t *testing.T) {
bls := NewSigAug()
pk, sks, err := bls.ThresholdKeygen(2, 2)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
// Test signing an empty message
_, err = bls.PartialSign(sks[0], pk, []byte{})
if err == nil {
t.Errorf("Expected partial sign of empty message to fail")
}
}
func TestAugPartialSignNilMessage(t *testing.T) {
bls := NewSigAug()
pk, sks, err := bls.ThresholdKeygen(7, 7)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
// Test signing a nil message
_, err = bls.PartialSign(sks[0], pk, nil)
if err == nil {
t.Errorf("Expected partial sign of nil message to fail")
}
}
@@ -0,0 +1,417 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"testing"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
)
func generateBasicSignatureG2(sk *SecretKey, msg []byte, t *testing.T) *Signature {
bls := NewSigBasic()
sig, err := bls.Sign(sk, msg)
if err != nil {
t.Errorf("Basic Sign failed")
}
return sig
}
func generateBasicAggregateDataG2(t *testing.T) ([]*PublicKey, []*Signature, [][]byte) {
msgs := make([][]byte, numAggregateG2)
pks := make([]*PublicKey, numAggregateG2)
sigs := make([]*Signature, numAggregateG2)
ikm := make([]byte, 32)
bls := NewSigBasic()
for i := 0; i < numAggregateG2; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Basic KeyGen failed")
}
msg := make([]byte, 20)
readRand(msg, t)
sig := generateBasicSignatureG2(sk, msg, t)
msgs[i] = msg
sigs[i] = sig
pks[i] = pk
}
return pks, sigs, msgs
}
func TestBasicKeyGenG2Works(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigBasic()
_, _, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Basic KeyGen failed")
}
}
func TestBasicKeyGenG2Fail(t *testing.T) {
ikm := make([]byte, 10)
readRand(ikm, t)
bls := NewSigBasic()
_, _, err := bls.KeygenWithSeed(ikm)
if err == nil {
t.Errorf("Basic KeyGen succeeded when it should've failed")
}
}
func TestBasicCustomDstG2(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigBasicWithDst("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_TEST")
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Basic Custom Dst KeyGen failed")
}
readRand(ikm, t)
sig, err := bls.Sign(sk, ikm)
if err != nil {
t.Errorf("Basic Custom Dst Sign failed")
}
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Basic Custon Dst verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Basic Custom Dst verify succeeded when it should've failed.")
}
}
func TestBasicSigningG2(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigBasic()
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Basic KeyGen failed")
}
readRand(ikm, t)
sig := generateBasicSignatureG2(sk, ikm, t)
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Basic verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Basic verify succeeded when it should've failed.")
}
}
func TestBasicSigningG2EmptyMessage(t *testing.T) {
// So basic
bls := NewSigBasic()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Basic KeyGen failed")
}
// Sign an empty message
_, err = bls.Sign(sk, []byte{})
if err != nil {
t.Errorf("Expected signing empty message to succeed: %v", err)
}
}
func TestBasicSigningG2NilMessage(t *testing.T) {
// So basic
bls := NewSigBasic()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Basic KeyGen failed")
}
// Sign an empty message
_, err = bls.Sign(sk, nil)
if err == nil {
t.Errorf("Expected signing nil message to fail")
}
}
func TestBasicAggregateVerifyG2Works(t *testing.T) {
pks, sigs, msgs := generateBasicAggregateDataG2(t)
bls := NewSigBasic()
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Basic AggregateVerify failed")
}
}
func TestBasicAggregateVerifyG2BadPks(t *testing.T) {
bls := NewSigBasic()
pks, sigs, msgs := generateBasicAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Basic AggregateVerify failed")
}
pks[0] = pks[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic AggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
pkValue := new(bls12381.G1).Identity()
pks[0] = &PublicKey{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic AggregateVerify succeeded with zero byte public key it should've failed")
}
// Try with base generator
pkValue.Generator()
pks[0] = &PublicKey{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf(
"Basic aggregateVerify succeeded with the base generator public key it should've failed",
)
}
}
func TestBasicAggregateVerifyG2BadSigs(t *testing.T) {
bls := NewSigBasic()
pks, sigs, msgs := generateBasicAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Basic aggregateVerify failed")
}
sigs[0] = sigs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic aggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
sigValue := new(bls12381.G2).Identity()
sigs[0] = &Signature{Value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic aggregateVerify succeeded with zero byte signature it should've failed")
}
// Try with base generator
sigValue.Generator()
sigs[0] = &Signature{Value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf(
"Basic aggregateVerify succeeded with the base generator signature it should've failed",
)
}
}
func TestBasicAggregateVerifyG2BadMsgs(t *testing.T) {
bls := NewSigBasic()
pks, sigs, msgs := generateBasicAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Basic aggregateVerify failed")
}
msgs[0] = msgs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Basic aggregateVerify succeeded when it should've failed")
}
}
func TestBasicThresholdKeygenBadInputs(t *testing.T) {
bls := NewSigBasic()
_, _, err := bls.ThresholdKeygen(0, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(1, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(3, 2)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
}
func TestBasicThresholdKeygen(t *testing.T) {
bls := NewSigBasic()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
if len(sks) != 5 {
t.Errorf("ThresholdKeygen did not produce enough shares")
}
}
func TestBasicPartialSign(t *testing.T) {
ikm := make([]byte, 32)
bls := NewSigBasic()
pk, sks, err := bls.ThresholdKeygenWithSeed(ikm, 2, 4)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
msg := make([]byte, 10)
sig1, err := bls.PartialSign(sks[0], msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig2, err := bls.PartialSign(sks[1], msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig, err := bls.CombineSignatures(sig1, sig2)
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("Combined signature does not verify")
}
sig, err = bls.CombineSignatures(sig1)
if err == nil {
t.Errorf("CombineSignatures succeeded when it should've failed")
}
if res, _ := bls.Verify(pk, msg, sig); res {
t.Errorf("Combined signature verify succeeded when it should've failed")
}
}
// Ensure that duplicate partial signatures cannot be used to create a complete one
func TestBasicPartialDuplicateShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigBasic()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignature, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining duplicates from group 1
sig, err := bls.CombineSignatures(sigs1[0], sigs1[0], sigs1[1], sigs1[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded")
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf("CombineSignatures worked with duplicate partial signatures")
}
}
// Ensure that mixed partial signatures from distinct origins create invalid composite signatures
func TestBasicPartialMixupShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigBasic()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
for i := range ikm {
ikm[i] = 1
}
pk2, sks2, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignature, total)
sigs2 := make([]*PartialSignature, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
sigs2[i], err = bls.PartialSign(sks2[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining 2 from group 1 and 2 from group 2
sig, err := bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[2], sigs2[3])
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
if res, _ := bls.Verify(pk2, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
// Should error out due to duplicate identifiers
_, err = bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[0], sigs2[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded.")
}
}
func TestIdentityPublicKey(t *testing.T) {
bls := NewSigBasic()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
msg := []byte{0, 0, 0, 0}
sig, _ := bls.Sign(sk, msg)
pk := PublicKey{value: *new(bls12381.G1).Identity()}
if res, _ := bls.Verify(&pk, msg, sig); res {
t.Errorf("Verify succeeded when the public key is the identity.")
}
}
func TestThresholdSignTooHighAndLow(t *testing.T) {
bls := NewSigBasic()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
msg := make([]byte, 10)
ps, err := bls.PartialSign(sks[0], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
_, err = bls.CombineSignatures(ps)
if err == nil {
t.Errorf("CombinSignatures succeeded when it should've failed")
}
pss := make([]*PartialSignature, 256)
_, err = bls.CombineSignatures(pss...)
if err == nil {
t.Errorf("CombinSignatures succeeded when it should've failed")
}
}
@@ -0,0 +1,936 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"bytes"
"math/big"
"math/rand"
"testing"
"github.com/sonr-io/sonr/crypto/core/curves/native/bls12381"
)
const numAggregateG2 = 10
func TestGetPublicKeyG1(t *testing.T) {
sk := genSecretKey(t)
pk := genPublicKey(sk, t)
actual := marshalStruct(pk, t)
expected := []byte{
166,
149,
173,
50,
93,
252,
126,
17,
145,
251,
201,
241,
134,
245,
142,
255,
66,
166,
52,
2,
151,
49,
177,
131,
128,
255,
137,
191,
66,
196,
100,
164,
44,
184,
202,
85,
178,
0,
240,
81,
245,
127,
30,
24,
147,
198,
135,
89,
}
if !bytes.Equal(actual, expected) {
t.Errorf("Expected GetPublicKey to pass but failed.")
}
}
func testSignG2(message []byte, t *testing.T) {
sk := genSecretKey(t)
sig := genSignature(sk, message, t)
pk := genPublicKey(sk, t)
bls := NewSigPop()
if res, err := bls.Verify(pk, message, sig); !res {
t.Errorf("createSignature failed when it should've passed: %v", err)
}
}
func TestSignG2EmptyMessage(t *testing.T) {
bls := NewSigPop()
sk := genSecretKey(t)
sig, _ := bls.Sign(sk, nil)
pk := genPublicKey(sk, t)
if res, _ := bls.Verify(pk, nil, sig); res {
t.Errorf("createSignature succeeded when it should've failed")
}
message := []byte{}
sig = genSignature(sk, message, t)
if res, err := bls.Verify(pk, message, sig); !res {
t.Errorf("create and verify failed on empty message: %v", err)
}
}
func TestSignG2OneByteMessage(t *testing.T) {
message := []byte{1}
testSignG2(message, t)
}
func TestSignG2LargeMessage(t *testing.T) {
message := make([]byte, 1048576)
testSignG2(message, t)
}
func TestSignG2RandomMessage(t *testing.T) {
message := make([]byte, 65537)
readRand(message, t)
testSignG2(message, t)
}
func TestSignG2BadMessage(t *testing.T) {
message := make([]byte, 1024)
sk := genSecretKey(t)
sig := genSignature(sk, message, t)
pk := genPublicKey(sk, t)
message = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
bls := NewSigPop()
if res, _ := bls.Verify(pk, message, sig); res {
t.Errorf("Expected signature to not verify")
}
}
func TestBadConversionsG2(t *testing.T) {
sk := genSecretKey(t)
message := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
sig := genSignature(sk, message, t)
pk := genPublicKey(sk, t)
bls := NewSigPop()
if res, _ := bls.Verify(pk, message, sig); !res {
t.Errorf("Signature should be valid")
}
if res, _ := sig.verify(pk, message, blsSignaturePopDst); !res {
t.Errorf("Signature should be valid")
}
// Convert public key to signature in G2
sig2 := new(SignatureVt)
err := sig2.UnmarshalBinary(marshalStruct(pk, t))
if err != nil {
t.Errorf("Should be able to convert to signature in G2")
}
pk2 := new(PublicKeyVt)
err = pk2.UnmarshalBinary(marshalStruct(sig, t))
if err != nil {
t.Errorf("Should be able to convert to public key in G1")
}
if res, _ := pk2.verifySignatureVt(message, sig2, blsSignaturePopDst); res {
t.Errorf("The signature shouldn't verify")
}
}
func TestAggregatePublicKeysG1(t *testing.T) {
pks := []*PublicKey{}
ikm := make([]byte, 32)
for i := 0; i < 20; i++ {
readRand(ikm, t)
sk := genRandSecretKey(ikm, t)
pk := genPublicKey(sk, t)
pks = append(pks, pk)
}
apk1, err := aggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
rng := rand.New(rand.NewSource(1234567890))
rng.Shuffle(len(pks), func(i, j int) { pks[i], pks[j] = pks[j], pks[i] })
apk2, err := aggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
if !bytes.Equal(marshalStruct(apk1, t), marshalStruct(apk2, t)) {
t.Errorf("Aggregated public keys should be equal")
}
rand.Shuffle(len(pks), func(i, j int) { pks[i], pks[j] = pks[j], pks[i] })
apk1, err = aggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
if !bytes.Equal(marshalStruct(apk1, t), marshalStruct(apk2, t)) {
t.Errorf("Aggregated public keys should be equal")
}
}
func TestAggregateSignaturesG2(t *testing.T) {
var sigs []*Signature
ikm := make([]byte, 32)
for i := 0; i < 20; i++ {
readRand(ikm, t)
sk := genRandSecretKey(ikm, t)
sig := genSignature(sk, ikm, t)
sigs = append(sigs, sig)
}
asig1, err := aggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
rng2 := rand.New(rand.NewSource(1234567890))
rng2.Shuffle(len(sigs), func(i, j int) { sigs[i], sigs[j] = sigs[j], sigs[i] })
asig2, err := aggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
if !bytes.Equal(marshalStruct(asig1, t), marshalStruct(asig2, t)) {
t.Errorf("Aggregated signatures should be equal")
}
rand.Shuffle(len(sigs), func(i, j int) { sigs[i], sigs[j] = sigs[j], sigs[i] })
asig1, err = aggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
if !bytes.Equal(marshalStruct(asig1, t), marshalStruct(asig2, t)) {
t.Errorf("Aggregated signatures should be equal")
}
}
func initAggregatedTestValuesG2(messages [][]byte, t *testing.T) ([]*PublicKey, []*Signature) {
pks := []*PublicKey{}
sigs := []*Signature{}
ikm := make([]byte, 32)
for i := 0; i < numAggregateG2; i++ {
readRand(ikm, t)
sk := genRandSecretKey(ikm, t)
sig := genSignature(sk, messages[i%len(messages)], t)
sigs = append(sigs, sig)
pk := genPublicKey(sk, t)
pks = append(pks, pk)
}
return pks, sigs
}
func TestAggregatedFunctionalityG2(t *testing.T) {
message := make([]byte, 20)
messages := make([][]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
asig, err := bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
apk, err := bls.AggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
if res, _ := bls.VerifyMultiSignature(apk, message, asig); !res {
t.Errorf("Should verify aggregated signatures with same message")
}
if res, _ := asig.verify(apk, message, blsSignaturePopDst); !res {
t.Errorf("MultiSignature.verify failed.")
}
if res, _ := apk.verify(message, asig, blsSignaturePopDst); !res {
t.Errorf("MultiPublicKey.verify failed.")
}
}
func TestBadAggregatedFunctionalityG2(t *testing.T) {
message := make([]byte, 20)
messages := make([][]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
apk, err := bls.AggregatePublicKeys(pks[2:]...)
if err != nil {
t.Errorf("%v", err)
}
asig, err := bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
if res, _ := bls.VerifyMultiSignature(apk, message, asig); res {
t.Errorf(
"Should not verify aggregated signatures with same message when some public keys are missing",
)
}
apk, err = bls.AggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
asig, err = bls.AggregateSignatures(sigs[2:]...)
if err != nil {
t.Errorf("%v", err)
}
if res, _ := bls.VerifyMultiSignature(apk, message, asig); res {
t.Errorf(
"Should not verify aggregated signatures with same message when some signatures are missing",
)
}
asig, err = bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
badmsg := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
if res, _ := bls.VerifyMultiSignature(apk, badmsg, asig); res {
t.Errorf("Should not verify aggregated signature with bad message")
}
}
func TestAggregateVerifyG2Pass(t *testing.T) {
messages := make([][]byte, numAggregateG2)
for i := 0; i < numAggregateG2; i++ {
message := make([]byte, 20)
readRand(message, t)
messages[i] = message
}
pks, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
if res, _ := bls.AggregateVerify(pks, messages, sigs); !res {
t.Errorf("Expected aggregateVerify to pass but failed")
}
}
func TestAggregateVerifyG2MsgSigCntMismatch(t *testing.T) {
messages := make([][]byte, 8)
for i := 0; i < 8; i++ {
message := make([]byte, 20)
readRand(message, t)
messages[i] = message
}
pks, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
if res, _ := bls.AggregateVerify(pks, messages, sigs); res {
t.Errorf("Expected AggregateVerifyG2 to fail with duplicate message but passed")
}
}
func TestAggregateVerifyG2FailDupMsg(t *testing.T) {
messages := make([][]byte, 10)
for i := 0; i < 9; i++ {
message := make([]byte, 20)
readRand(message, t)
messages[i] = message
}
// Duplicate message
messages[9] = messages[0]
pks, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
if res, _ := bls.AggregateVerify(pks, messages, sigs); res {
t.Errorf("Expected aggregateVerify to fail with duplicate message but passed")
}
}
func TestAggregateVerifyG2FailIncorrectMsg(t *testing.T) {
messages := make([][]byte, 10)
for i := 0; i < 9; i++ {
message := make([]byte, 20)
readRand(message, t)
messages[i] = message
}
// Duplicate message
messages[9] = messages[0]
pks, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
if res, _ := bls.AggregateVerify(pks[2:], messages[2:], sigs); res {
t.Errorf("Expected aggregateVerify to fail with duplicate message but passed")
}
}
func TestAggregateVerifyG2OneMsg(t *testing.T) {
messages := make([][]byte, 1)
messages[0] = make([]byte, 20)
sk := genSecretKey(t)
sig := genSignature(sk, messages[0], t)
pk := genPublicKey(sk, t)
bls := NewSigPop()
// Should be the same as verifySignature
if res, _ := bls.AggregateVerify([]*PublicKey{pk}, messages, []*Signature{sig}); !res {
t.Errorf("Expected AggregateVerifyG2OneMsg to pass but failed")
}
}
func TestVerifyG2Mutability(t *testing.T) {
// verify should not change any inputs
ikm := make([]byte, 32)
ikm_copy := make([]byte, 32)
readRand(ikm, t)
copy(ikm_copy, ikm)
bls := NewSigPop()
pk, sk, err := bls.KeygenWithSeed(ikm)
if !bytes.Equal(ikm, ikm_copy) {
t.Errorf("SigPop.KeygenWithSeed modifies ikm")
}
if err != nil {
t.Errorf("Expected KeygenWithSeed to succeed but failed.")
}
sig, err := bls.Sign(sk, ikm)
if !bytes.Equal(ikm, ikm_copy) {
t.Errorf("SigPop.Sign modifies message")
}
if err != nil {
t.Errorf("SigPop.KeygenWithSeed to succeed but failed.")
}
sigCopy := marshalStruct(sig, t)
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Expected verify to succeed but failed.")
}
if !bytes.Equal(ikm, ikm_copy) {
t.Errorf("SigPop.verify modifies message")
}
if !bytes.Equal(sigCopy, marshalStruct(sig, t)) {
t.Errorf("SigPop.verify modifies signature")
}
}
func TestPublicKeyG1FromBadBytes(t *testing.T) {
pk := make([]byte, 32)
err := new(PublicKey).UnmarshalBinary(pk)
if err == nil {
t.Errorf("Expected PublicKeyG1FromBytes to fail but passed")
}
// All zeros
pk = make([]byte, PublicKeySize)
// See https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// 1 << 7 == compressed
// 1 << 6 == infinity or zero
pk[0] = 0xc0
err = new(PublicKey).UnmarshalBinary(pk)
if err == nil {
t.Errorf("Expected PublicKeyG1FromBytes to fail but passed")
}
sk := genSecretKey(t)
pk1, err := sk.GetPublicKey()
if err != nil {
t.Errorf("Expected GetPublicKey to pass but failed.")
}
out := marshalStruct(pk1, t)
out[3] += 1
err = new(PublicKey).UnmarshalBinary(pk)
if err == nil {
t.Errorf("Expected PublicKeyG1FromBytes to fail but passed")
}
}
func TestSignatureG2FromBadBytes(t *testing.T) {
sig := make([]byte, 32)
err := new(Signature).UnmarshalBinary(sig)
if err == nil {
t.Errorf("Expected SignatureG2FromBytes to fail but passed")
}
// All zeros
sig = make([]byte, SignatureSize)
// See https://github.com/zcash/librustzcash/blob/master/pairing/src/bls12_381/README.md#serialization
// 1 << 7 == compressed
// 1 << 6 == infinity or zero
sig[0] = 0xc0
err = new(Signature).UnmarshalBinary(sig)
if err == nil {
t.Errorf("Expected SignatureG2FromBytes to fail but passed")
}
}
func TestBadSecretKeyG2(t *testing.T) {
sk := &SecretKey{value: bls12381.Bls12381FqNew()}
pk, err := sk.GetPublicKey()
if err == nil {
t.Errorf("Expected GetPublicKey to fail with 0 byte secret key but passed: %v", pk)
}
_ = sk.UnmarshalBinary(sk.value.Params.BiModulus.Bytes())
pk, err = sk.GetPublicKey()
if err == nil {
t.Errorf("Expected GetPublicKey to fail with secret key with Q but passed: %v", pk)
}
err = sk.UnmarshalBinary([]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16})
if err == nil {
t.Errorf("Expected SecretKeyFromBytes to fail with not enough bytes but passed: %v", pk)
}
err = sk.UnmarshalBinary(make([]byte, 32))
if err == nil {
t.Errorf("Expected SecretKeyFromBytes to fail with all zeros but passed: %v", pk)
}
}
func TestProofOfPossessionG2Works(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigPop()
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Key gen failed but should've succeeded")
}
pop, err := bls.PopProve(sk)
if err != nil {
t.Errorf("PopProve failed but should've succeeded")
}
if res, _ := bls.PopVerify(pk, pop); !res {
t.Errorf("PopVerify failed but should've succeeded")
}
}
func TestProofOfPossessionG2FromBadKey(t *testing.T) {
ikm := make([]byte, 32)
value := new(big.Int)
value.SetBytes(ikm)
sk := SecretKey{value: bls12381.Bls12381FqNew().SetBigInt(value)}
_, err := sk.createProofOfPossession(blsSignaturePopDst)
if err == nil {
t.Errorf("createProofOfPossession should've failed but succeeded.")
}
}
func TestProofOfPossessionG2BytesWorks(t *testing.T) {
sk := genSecretKey(t)
pop, err := sk.createProofOfPossession(blsSignaturePopDst)
if err != nil {
t.Errorf("CreateProofOfPossesionG2 failed but shouldn've succeeded.")
}
out := marshalStruct(pop, t)
if len(out) != ProofOfPossessionSize {
t.Errorf(
"ProofOfPossessionBytes incorrect size: expected %v, got %v",
ProofOfPossessionSize,
len(out),
)
}
pop2 := new(ProofOfPossession)
err = pop2.UnmarshalBinary(out)
if err != nil {
t.Errorf("ProofOfPossession.UnmarshalBinary failed: %v", err)
}
out2 := marshalStruct(pop2, t)
if !bytes.Equal(out, out2) {
t.Errorf("ProofOfPossession.UnmarshalBinary failed, not equal when deserialized")
}
}
func TestProofOfPossessionG2BadBytes(t *testing.T) {
zeros := make([]byte, ProofOfPossessionSize)
temp := new(ProofOfPossession)
err := temp.UnmarshalBinary(zeros)
if err == nil {
t.Errorf("ProofOfPossession.UnmarshalBinary shouldn've failed but succeeded.")
}
}
func TestProofOfPossessionG2Fails(t *testing.T) {
sk := genSecretKey(t)
pop, err := sk.createProofOfPossession(blsSignaturePopDst)
if err != nil {
t.Errorf("Expected createProofOfPossession to succeed but failed.")
}
ikm := make([]byte, 32)
readRand(ikm, t)
sk = genRandSecretKey(ikm, t)
bad, err := sk.GetPublicKey()
if err != nil {
t.Errorf("Expected PublicKeyG1FromBytes to succeed but failed: %v", err)
}
if res, _ := pop.verify(bad, blsSignaturePopDst); res {
t.Errorf("Expected ProofOfPossession verify to fail but succeeded.")
}
}
func TestMultiSigG2Bytes(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 20)
messages[0] = message
_, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
msig, err := bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
msigBytes := marshalStruct(msig, t)
if len(msigBytes) != SignatureSize {
t.Errorf(
"Invalid multi-sig length. Expected %d bytes, found %d",
SignatureSize,
len(msigBytes),
)
}
msig2 := new(MultiSignature)
err = msig2.UnmarshalBinary(msigBytes)
if err != nil {
t.Errorf("MultiSignatureG2FromBytes failed with %v", err)
}
msigBytes2 := marshalStruct(msig2, t)
if !bytes.Equal(msigBytes, msigBytes2) {
t.Errorf("Bytes methods not equal.")
}
}
func TestMultiSigG2BadBytes(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 20)
messages[0] = message
_, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
msig, err := bls.AggregateSignatures(sigs...)
if err != nil {
t.Errorf("%v", err)
}
msigBytes := marshalStruct(msig, t)
if len(msigBytes) != SignatureSize {
t.Errorf(
"Invalid multi-sig length. Expected %d bytes, found %d",
SignatureSize,
len(msigBytes),
)
}
msigBytes[0] = 0
temp := new(MultiSignature)
err = temp.UnmarshalBinary(msigBytes)
if err == nil {
t.Errorf("MultiSignatureG2FromBytes should've failed but succeeded")
}
msigBytes = make([]byte, SignatureSize)
err = temp.UnmarshalBinary(msigBytes)
if err == nil {
t.Errorf("MultiSignatureG2FromBytes should've failed but succeeded")
}
}
func TestMultiPubkeyG1Bytes(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 20)
messages[0] = message
pks, _ := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
apk, err := bls.AggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
apkBytes := marshalStruct(apk, t)
if len(apkBytes) != PublicKeySize {
t.Errorf("MultiPublicKey has an incorrect size")
}
apk2 := new(MultiPublicKey)
err = apk2.UnmarshalBinary(apkBytes)
if err != nil {
t.Errorf("MultiPublicKey.UnmarshalBinary failed with %v", err)
}
apk2Bytes := marshalStruct(apk2, t)
if !bytes.Equal(apkBytes, apk2Bytes) {
t.Errorf("Bytes methods not equal.")
}
}
func TestMultiPubkeyG1BadBytes(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 20)
messages[0] = message
pks, _ := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
apk, err := bls.AggregatePublicKeys(pks...)
if err != nil {
t.Errorf("%v", err)
}
apkBytes := marshalStruct(apk, t)
if len(apkBytes) != PublicKeySize {
t.Errorf("MultiPublicKey has an incorrect size")
}
apkBytes[0] = 0
temp := new(MultiPublicKey)
err = temp.UnmarshalBinary(apkBytes)
if err == nil {
t.Errorf("MultiPublicKey.UnmarshalBinary should've failed but succeeded")
}
apkBytes = make([]byte, PublicKeySize)
err = temp.UnmarshalBinary(apkBytes)
if err == nil {
t.Errorf("MultiPublicKey.UnmarshalBinary should've failed but succeeded")
}
}
func TestFastAggregateVerifyG2Works(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG2(messages, t)
asigs, _ := aggregateSignatures(sigs...)
bls := NewSigPop()
if res, _ := bls.FastAggregateVerify(pks, message, asigs); !res {
t.Errorf("FastAggregateVerify failed.")
}
}
func TestFastAggregateVerifyConstituentG2Works(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
if res, _ := bls.FastAggregateVerifyConstituent(pks, message, sigs); !res {
t.Errorf("FastAggregateVerify failed.")
}
}
func TestFastAggregateVerifyG2Fails(t *testing.T) {
messages := make([][]byte, 1)
message := make([]byte, 1)
messages[0] = message
pks, sigs := initAggregatedTestValuesG2(messages, t)
bls := NewSigPop()
message[0] = 1
if res, _ := bls.FastAggregateVerifyConstituent(pks, message, sigs); res {
t.Errorf("FastAggregateVerify verified when it should've failed.")
}
}
func TestCustomPopDstG2Works(t *testing.T) {
bls, _ := NewSigPopWithDst("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_TEST",
"BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_TEST")
msg := make([]byte, 20)
ikm := make([]byte, 32)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Couldn't create custom dst keys: %v", err)
}
sig, err := bls.Sign(sk, msg)
if err != nil {
t.Errorf("Couldn't sign with custom dst: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("verify fails with custom dst")
}
pks := make([]*PublicKey, 10)
sigs := make([]*Signature, 10)
pks[0] = pk
sigs[0] = sig
for i := 1; i < 10; i++ {
readRand(ikm, t)
pkt, skt, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Couldn't create custom dst keys: %v", err)
}
sigt, err := bls.Sign(skt, msg)
if err != nil {
t.Errorf("Couldn't sign with custom dst: %v", err)
}
pks[i] = pkt
sigs[i] = sigt
}
if res, _ := bls.FastAggregateVerifyConstituent(pks, msg, sigs); !res {
t.Errorf("FastAggregateVerify failed with custom dst")
}
pop, err := bls.PopProve(sk)
if err != nil {
t.Errorf("PopProve failed with custom dst")
}
if res, _ := bls.PopVerify(pk, pop); !res {
t.Errorf("PopVerify failed with custom dst")
}
}
func TestBlsPopG2KeyGenWithSeed(t *testing.T) {
ikm := []byte("Not enough bytes")
bls := NewSigPop()
_, _, err := bls.KeygenWithSeed(ikm)
if err == nil {
t.Errorf("Expected KeygenWithSeed to fail but succeeded")
}
}
func TestBlsPopG2KeyGen(t *testing.T) {
bls := NewSigPop()
_, _, err := bls.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
}
func TestPopThresholdKeygenBadInputs(t *testing.T) {
bls := NewSigPop()
_, _, err := bls.ThresholdKeygen(0, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(1, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(3, 2)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
}
func TestPopThresholdKeygen(t *testing.T) {
bls := NewSigPop()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
if len(sks) != 5 {
t.Errorf("ThresholdKeygen did not produce enough shares")
}
}
func TestPopPartialSign(t *testing.T) {
ikm := make([]byte, 32)
bls := NewSigPop()
pk, sks, err := bls.ThresholdKeygenWithSeed(ikm, 2, 4)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
msg := make([]byte, 10)
sig1, err := bls.PartialSign(sks[0], msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig2, err := bls.PartialSign(sks[1], msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig, err := bls.CombineSignatures(sig1, sig2)
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("Combined signature does not verify")
}
sig, err = bls.CombineSignatures(sig1)
if err == nil {
t.Errorf("CombineSignatures succeeded when it should've failed")
}
if res, _ := bls.Verify(pk, msg, sig); res {
t.Errorf("Combined signature verify succeeded when it should've failed")
}
}
// Ensure that mixed partial signatures from distinct origins create invalid composite signatures
func TestPopPartialMixupShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigPop()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
for i := range ikm {
ikm[i] = 1
}
pk2, sks2, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignature, total)
sigs2 := make([]*PartialSignature, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
sigs2[i], err = bls.PartialSign(sks2[i], msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining 2 from group 1 and 2 from group 2
sig, err := bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[2], sigs2[3])
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
if res, _ := bls.Verify(pk2, msg, sig); res {
t.Errorf(
"CombineSignatures worked with different shares of two secret keys for the same message",
)
}
// Should error out due to duplicate identifiers
_, err = bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[0], sigs2[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded.")
}
}
func TestNewSigEth2KeyGen(t *testing.T) {
eth2 := NewSigEth2()
_, _, err := eth2.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
}
func TestSigEth2SignRoundTrip(t *testing.T) {
eth2 := NewSigEth2()
pop := NewSigPop()
eth2Pk, eth2Sk, err := eth2.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
sig, err := pop.Sign(eth2Sk, []byte{0, 0})
if err != nil {
t.Errorf("Sign failed: %v", err)
}
if ok, err := eth2.Verify(eth2Pk, []byte{0, 0}, sig); err != nil || !ok {
t.Errorf("Verify failed: %v", err)
}
}
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "miracl"
version = "0.1.0"
authors = ["Mike Lodder <mike.lodder@coinbase.com>"]
edition = "2018"
[dependencies]
bls_sigs_ref = "0.3"
hex = "0.4"
miracl_core = { version = "2.3", features = [
"bls12381",
], default-features = false }
pairing-plus = "0.19"
rand = "0.8"
serious = { version = "0.1", git = "https://github.com/mikelodder7/malutils" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha2 = "0.8"
structopt = "0.3"
+13
View File
@@ -0,0 +1,13 @@
---
aliases: [README]
tags: []
title: README
linter-yaml-title-alias: README
date created: Wednesday, April 17th 2024, 4:11:40 pm
date modified: Thursday, April 18th 2024, 8:19:25 am
---
## Note
This rust package is added for the sole purpose of comparing the result of our Go package with other
implementations, including the rust package.
+388
View File
@@ -0,0 +1,388 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
use bls_sigs_ref::BLSSignaturePop;
use miracl_core::bls12381::{big::BIG, ecp::ECP, ecp2::ECP2, rom::MODULUS};
use pairing_plus::{
bls12_381::{Fr, G1, G2},
hash_to_field::BaseFromRO,
serdes::SerDes,
CurveProjective,
};
use rand::prelude::*;
use serde::Deserialize;
use serious::Encoding;
use sha2::digest::generic_array::GenericArray;
use std::{
fs::File,
io::{self, Cursor, Read},
path::PathBuf,
};
use structopt::StructOpt;
fn main() {
let args = CliArgs::from_args();
match args {
CliArgs::Generate { number } => generate(number),
CliArgs::PublicKey { keys } => pubkey(keys),
CliArgs::Sign { number, data } => sign(number, data),
CliArgs::Verify { keys } => verify(keys),
}
}
fn generate(number: usize) {
let mut rng = thread_rng();
print!("[");
let mut sep = "";
for _ in 0..number {
let mut buf = [0u8; 48];
rng.fill_bytes(&mut buf);
// let mut sk = buf;
let fr = Fr::from_okm(GenericArray::from_slice(&buf));
let mut pk = G1::one();
pk.mul_assign(fr);
pk.serialize(&mut buf.as_mut(), true).unwrap();
print!("{}\"{}\"", sep, hex::encode(buf));
sep = ",";
// Miracl expects 48 bytes for secret key even though the top 16 bytes are zeros
// sk = [0u8; 48];
// fr.serialize(&mut sk[16..].as_mut(), true).unwrap();
// let s = BIG::frombytes(&sk[..]);
// let x = _compress_g1(g1mul(&ECP::generator(), &s));
//
// println!("Miracl = {}", hex::encode(x));
}
print!("]");
}
/// Compress to BLS12-381 standard vs ANSI X9.62
fn _compress_g1(pk: ECP) -> [u8; 48] {
let mut x = [0u8; 48];
pk.getx().tobytes(&mut x);
if pk.is_infinity() {
// Set the second-most significant bit to indicate this point
// is at infinity.
x[0] |= 1 << 6;
} else {
let m = BIG { w: MODULUS };
let mut negy = BIG::new();
negy.add(&BIG::modneg(&pk.gety(), &m));
negy.rmod(&m);
negy.norm();
// Set the third most significant bit if the correct y-coordinate
// is lexicographically largest.
if BIG::comp(&pk.gety(), &negy) == 1 {
x[0] |= 1 << 5;
}
}
// Set highest bit to distinguish this as a compressed element.
x[0] |= 1 << 7;
x
}
fn _compress_g2(sig: ECP2) -> [u8; 96] {
let mut x = [0u8; 96];
sig.getx().geta().tobytes(&mut x[..48]);
sig.getx().getb().tobytes(&mut x[48..]);
if sig.is_infinity() {
// Set the second-most significant bit to indicate this point
// is at infinity.
x[0] |= 1 << 6;
} else {
let mut negy = sig.clone();
negy.neg();
// Set the third most significant bit if the correct y-coordinate
// is lexicographically largest.
negy.sub(&sig);
if negy.gety().sign() > 0 {
x[0] |= 1 << 5;
}
}
// Set highest bit to distinguish this as a compressed element.
x[0] |= 1 << 7;
x
}
fn pubkey(keys: String) {
let res = read_input(&keys).unwrap();
for pubkey in serde_json::from_slice::<Vec<String>>(&res).unwrap() {
let res = hex::decode(&pubkey);
if res.is_err() {
println!("Invalid hex format {}", res.unwrap_err());
continue;
}
let mut key = res.unwrap();
let mut cur = Cursor::new(key.as_slice());
print!("ZCash {} - ", pubkey);
let res = G1::deserialize(&mut cur, true);
if let Err(e) = res {
println!("fail - {}", e);
} else {
println!("pass");
}
let res = _uncompress_g1(key.as_mut_slice());
print!("Miracl {} - ", pubkey);
if let Err(e) = res {
println!("fail - {}", e);
} else {
println!("pass");
}
}
}
fn _uncompress_g1(d: &[u8]) -> Result<ECP, String> {
if d.len() != 48 {
return Err("Invalid length".to_string());
}
if d[0] & 0x80 != 0x80 {
return Err("Expected compressed point".to_string());
}
// Expect point at infinity
if d[0] & 0x40 == 0x40 {
return if !d.iter().skip(1).all(|b| *b == 0) {
Err("Expected point at infinity but found another point".to_string())
} else {
Ok(ECP::new())
};
}
let s = d[0] & 0x20;
// Unset top bits
let mut dd = [0u8; 48];
dd.copy_from_slice(d);
dd[0] &= 0x1F;
let x = BIG::frombytes(&dd);
Ok(ECP::new_bigint(&x, s as isize))
}
fn _uncompress_g2(d: &[u8]) -> Result<ECP2, String> {
if d.len() != 96 {
return Err("Invalid length".to_string());
}
if d[0] & 0x80 != 0x80 {
return Err("Expected compressed point".to_string());
}
// Expect point at infinity
if d[0] & 0x40 == 0x40 {
return if !d.iter().skip(1).all(|b| *b == 0) {
Err("Expected point at infinity but found another point".to_string())
} else {
Ok(ECP2::new())
};
}
let s = d[0] & 0x20;
let mut dd = [0u8; 97];
dd[1..].copy_from_slice(d);
dd[1] &= 0x1F;
// Unset top bits
dd[0] = if s > 0 { 0x3 } else { 0x2 };
Ok(ECP2::frombytes(&dd))
}
fn sign(number: usize, data: String) {
let mut rng = thread_rng();
let bytes = data.as_bytes();
let mut sep = "";
print!("[");
for _ in 0..number {
let mut buf = [0u8; 48];
rng.fill_bytes(&mut buf);
let fr = Fr::from_okm(GenericArray::from_slice(&buf));
let mut pk = G1::one();
pk.mul_assign(fr);
let mut pubkey = [0u8; 48];
pk.serialize(&mut pubkey.as_mut(), true).unwrap();
let signature = G2::sign(fr, bytes);
let mut sig = [0u8; 96];
signature.serialize(&mut sig.as_mut(), true).unwrap();
print!("{}{{", sep);
print!(r#""data":"{}","#, data);
print!(
r#""public_key":"{}","#,
Encoding::encode(pubkey, Encoding::LowHex).into_string()
);
print!(
r#""signature":"{}""#,
Encoding::encode(sig, Encoding::LowHex).into_string()
);
print!("}}");
sep = ",";
}
print!("]");
}
fn verify(keys: String) {
let res = read_input(&keys).unwrap();
for req in serde_json::from_slice::<Vec<VerifyRequest>>(&res).unwrap() {
let pubkey = Encoding::decode(&req.public_key, Encoding::LowHex).unwrap();
let sig = Encoding::decode(&req.signature, Encoding::LowHex).unwrap();
let mut cur = Cursor::new(pubkey.as_slice());
let verkey = G1::deserialize(&mut cur, true).unwrap();
cur = Cursor::new(sig.as_slice());
let signature = G2::deserialize(&mut cur, true).unwrap();
print!("ZCash {} - ", req.public_key);
if G2::verify(verkey, signature, req.data.as_bytes()) {
println!("pass");
} else {
println!("fail");
}
}
}
#[derive(Debug, StructOpt)]
enum CliArgs {
Generate {
#[structopt(short, long)]
number: usize,
},
PublicKey {
#[structopt(name = "KEYS")]
keys: String,
},
Sign {
#[structopt(short, long)]
number: usize,
#[structopt(short, long)]
data: String,
},
Verify {
#[structopt(name = "KEYS")]
keys: String,
},
}
#[derive(Deserialize)]
struct VerifyRequest {
data: String,
public_key: String,
signature: String,
}
fn read_input(value: &str) -> Result<Vec<u8>, String> {
if !value.is_empty() {
match get_file(value) {
Some(file) => match File::open(file.as_path()) {
Ok(mut f) => Ok(read_stream(&mut f)),
Err(_) => Err(format!("Unable to read file {}", file.to_str().unwrap())),
},
None => Ok(value.as_bytes().to_vec()),
}
} else {
let mut f = io::stdin();
Ok(read_stream(&mut f))
}
}
fn read_stream<R: Read>(f: &mut R) -> Vec<u8> {
let mut bytes = Vec::new();
let mut buffer = [0u8; 4096];
let mut read = f.read(&mut buffer);
while read.is_ok() {
let n = read.unwrap();
if n == 0 {
break;
}
bytes.extend_from_slice(&buffer[..n]);
read = f.read(&mut buffer);
}
bytes
}
fn get_file(name: &str) -> Option<PathBuf> {
if name.len() > 256 {
// too long to be a file
return None;
}
let mut file = PathBuf::new();
file.push(name);
if file.as_path().is_file() {
let metadata = file
.as_path()
.symlink_metadata()
.expect("symlink_metadata call failed");
if metadata.file_type().is_symlink() {
if let Ok(f) = file.as_path().read_link() {
file = f
} else {
return None;
}
}
Some(file)
} else {
None
}
}
// fn from_encoding(src: &str) -> Result<Encoding, std::io::Error> {
// Encoding::parse(src).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))
// }
// fn hash_to_g2(d: &[u8]) -> ECP2 {
// const DST: &'static str = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_";
// let y = <G2 as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(d.as_ref(), DST);
// let mut d = [0u8; 193];
// d[0] = 0x4;
// y.serialize(&mut d[1..].as_mut(), false);
// ECP2::frombytes(&d)
// }
// fn ceil(a: usize, b: usize) -> usize {
// (a-1)/b+1
// }
//
// fn hash_to_field(hash: usize, hlen: usize, u: &mut [FP2], dst: &[u8], m: &[u8], ctr: usize) {
// let q = BIG { w: MODULUS };
// let el = ceil(q.nbits()+AESKEY*16, 8);
//
// let mut okm = [0u8; 512];
// let mut fd = [0u8; 256];
// xmd_expand(hash, hlen, &mut okm, el*ctr, &dst, &m);
// u[0] = FP2::new_fps(
// &FP::new_big(&DBIG::frombytes(&okm[0..el]).dmod(&q)),
// &FP::new_big(&DBIG::frombytes(&okm[el..(2*el)]).dmod(&q))
// );
// u[1] = FP2::new_fps(
// &FP::new_big(&DBIG::frombytes(&okm[(2*el)..(3*el)]).dmod(&q)),
// &FP::new_big(&DBIG::frombytes(&okm[(3*el)..]).dmod(&q))
// );
// }
//
// fn hash_to_ecp2(m: &[u8]) -> ECP2 {
// let dst = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_";
// let mut u = [FP2::new(); 2];
// hash_to_field(hmac::MC_SHA2, ecp::HASH_TYPE, &mut u, &dst[..], m, 2);
//
// let mut p = ECP2::map2point(&u[0]);
// let q = ECP2::map2point(&u[1]);
// p.add(&q);
// p.cfp();
// p.affine();
// p
// }
+236
View File
@@ -0,0 +1,236 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"bufio"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"os"
bls "github.com/sonr-io/sonr/crypto/signatures/bls/bls_sig"
)
type signOp struct {
Data string `json:"data"`
PublicKey string `json:"public_key"`
Signature string `json:"signature"`
}
type cmdFlags struct {
Generate bool
PublicKeys bool
Sign bool
Verify bool
Number int
}
func parseCliArgs() cmdFlags {
var generate, publickeys, sign, verify bool
var number int
flag.BoolVar(&generate, "g", false, "Generate public keys")
flag.BoolVar(&publickeys, "p", false, "Verify public keys")
flag.BoolVar(&sign, "s", false, "Generate signatures")
flag.BoolVar(&verify, "v", false, "Verify signatures")
flag.IntVar(&number, "n", 25, "The number of items to generate")
flag.Parse()
return cmdFlags{
generate, publickeys, sign, verify, number,
}
}
func main() {
flags := parseCliArgs()
if flags.Generate {
generate(flags.Number)
} else if flags.PublicKeys {
publicKeys()
} else if flags.Sign {
sign(flags.Number)
} else if flags.Verify {
verify()
}
}
func generate(number int) {
scheme := bls.NewSigPop()
publicKeys := make([]string, number)
for i := 0; i < number; i++ {
publicKey, _, err := scheme.Keygen()
if err != nil {
fmt.Printf("Keygen error occurred: %v\n", err)
os.Exit(1)
}
b, _ := publicKey.MarshalBinary()
publicKeys[i] = hex.EncodeToString(b)
}
out, _ := json.Marshal(publicKeys)
fmt.Println(string(out))
}
func publicKeys() {
input, err := getInput()
if err != nil {
fmt.Printf("Unable to read input: %v", err)
os.Exit(1)
}
var pubkeys []string
err = json.Unmarshal(input, &pubkeys)
if err != nil {
fmt.Printf("Unable to parse json input: %v", err)
os.Exit(1)
}
fmt.Printf("Checking public keys")
for _, pk := range pubkeys {
data, err := hex.DecodeString(pk)
if err != nil {
fmt.Printf("Unable to parse hex input: %v", err)
os.Exit(1)
}
pubkey := new(bls.PublicKey)
fmt.Printf("Checking %s - ", pk)
err = pubkey.UnmarshalBinary(data)
if err != nil {
fmt.Printf("fail\n")
os.Exit(1)
}
fmt.Printf("pass\n")
}
}
func sign(number int) {
tests := []string{
"", "aaa", "aaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
}
var err error
scheme := bls.NewSigPop()
secretKeys := make([]*bls.SecretKey, number)
publicKeys := make([]*bls.PublicKey, number)
for i := 0; i < number; i++ {
publicKeys[i], secretKeys[i], err = scheme.Keygen()
if err != nil {
fmt.Printf("Keygen error occurred: %v\n", err)
os.Exit(1)
}
}
sigs := make([]*signOp, number*len(tests))
k := 0
for _, t := range tests {
for j := 0; j < len(secretKeys); j++ {
signature, err := scheme.Sign(secretKeys[j], []byte(t))
if err != nil {
fmt.Printf("Signing failed: %v\n", err)
os.Exit(1)
}
pk, _ := publicKeys[j].MarshalBinary()
sig, _ := signature.MarshalBinary()
sigs[k] = &signOp{
Data: t,
PublicKey: hex.EncodeToString(pk),
Signature: hex.EncodeToString(sig),
}
k++
}
}
data, err := json.Marshal(sigs)
if err != nil {
fmt.Printf("Unable to convert signatures to json")
os.Exit(1)
}
fmt.Print(string(data))
}
func verify() {
input, err := getInput()
if err != nil {
fmt.Printf("Unable to read input: %v", err)
os.Exit(1)
}
var operations []signOp
err = json.Unmarshal(input, &operations)
if err != nil {
fmt.Printf("Unable to parse json input: %v", err)
os.Exit(1)
}
scheme := bls.NewSigPop()
for _, op := range operations {
fmt.Printf("Checking %s - ", op.PublicKey)
data, err := hex.DecodeString(op.PublicKey)
if err != nil {
fmt.Printf("fail. Unable to parse hex input: %v", err)
os.Exit(1)
}
pubkey := new(bls.PublicKey)
err = pubkey.UnmarshalBinary(data)
if err != nil {
fmt.Printf("fail. Invalid public key: %v", err)
os.Exit(1)
}
data, err = hex.DecodeString(op.Signature)
if err != nil {
fmt.Printf("fail. Unable to parse hex input: %v", err)
os.Exit(1)
}
sig := new(bls.Signature)
err = sig.UnmarshalBinary(data)
if err != nil {
fmt.Printf("fail. Invalid signature format: %v", err)
os.Exit(1)
}
ok, err := scheme.Verify(pubkey, []byte(op.Data), sig)
if !ok || err != nil {
fmt.Printf("fail. Invalid signature")
os.Exit(1)
}
fmt.Printf("pass.")
}
}
func getInput() ([]byte, error) {
fi, _ := os.Stdin.Stat()
var data []byte
if (fi.Mode() & os.ModeCharDevice) == 0 {
// Read from pipe
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
data = append(data, scanner.Bytes()...)
}
if err := scanner.Err(); err != nil {
return nil, err
}
} else {
// Read from file
arguments := flag.Args()
if len(arguments) < 1 {
return nil, fmt.Errorf("expected data argument")
}
_, err := os.Stat(arguments[0])
if os.IsNotExist(err) {
data = []byte(arguments[0])
} else {
contents, err := os.ReadFile(arguments[0])
if err != nil {
return nil, err
}
data = contents
}
}
return data, nil
}