mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-04 18:31:41 +00:00
(no commit message provided)
This commit is contained in:
Executable
+208
@@ -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/onsonr/hway/crypto/core/curves"
|
||||
"github.com/onsonr/hway/crypto/core/curves/native"
|
||||
"github.com/onsonr/hway/crypto/core/curves/native/bls12381"
|
||||
"github.com/onsonr/hway/crypto/internal"
|
||||
"github.com/onsonr/hway/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
|
||||
}
|
||||
Executable
+284
@@ -0,0 +1,284 @@
|
||||
//
|
||||
// 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/onsonr/hway/crypto/core/curves/native/bls12381"
|
||||
"github.com/onsonr/hway/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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+438
@@ -0,0 +1,438 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
+489
@@ -0,0 +1,489 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package bls_sig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/hway/crypto/core/curves/native"
|
||||
"github.com/onsonr/hway/crypto/core/curves/native/bls12381"
|
||||
"github.com/onsonr/hway/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
|
||||
}
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package bls_sig
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/onsonr/hway/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")
|
||||
}
|
||||
}
|
||||
+377
@@ -0,0 +1,377 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package bls_sig
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/onsonr/hway/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")
|
||||
}
|
||||
}
|
||||
+845
@@ -0,0 +1,845 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package bls_sig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/onsonr/hway/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)
|
||||
}
|
||||
rand.Seed(1234567890)
|
||||
rand.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)
|
||||
}
|
||||
rand.Seed(1234567890)
|
||||
rand.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.")
|
||||
}
|
||||
}
|
||||
Executable
+446
@@ -0,0 +1,446 @@
|
||||
//
|
||||
// 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)
|
||||
}
|
||||
+491
@@ -0,0 +1,491 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package bls_sig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/hway/crypto/core/curves/native"
|
||||
"github.com/onsonr/hway/crypto/core/curves/native/bls12381"
|
||||
"github.com/onsonr/hway/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
|
||||
}
|
||||
+409
@@ -0,0 +1,409 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package bls_sig
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/onsonr/hway/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")
|
||||
}
|
||||
}
|
||||
+409
@@ -0,0 +1,409 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package bls_sig
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/onsonr/hway/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")
|
||||
}
|
||||
}
|
||||
+867
@@ -0,0 +1,867 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package bls_sig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/onsonr/hway/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)
|
||||
}
|
||||
rand.Seed(1234567890)
|
||||
rand.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)
|
||||
}
|
||||
rand.Seed(1234567890)
|
||||
rand.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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user