feature/1114 implement account interface (#1167)

- **refactor: move session-related code to middleware package**
- **refactor: update PKL build process and adjust related
configurations**
- **feat: integrate base.cosmos.v1 Genesis module**
- **refactor: pass session context to modal rendering functions**
- **refactor: move nebula package to app directory and update templ
version**
- **refactor: Move home section video view to dedicated directory**
- **refactor: remove unused views file**
- **refactor: move styles and UI components to global scope**
- **refactor: Rename images.go to cdn.go**
- **feat: Add Empty State Illustrations**
- **refactor: Consolidate Vault Index Logic**
- **fix: References to App.wasm and remove Vault Directory embedded CDN
files**
- **refactor: Move CDN types to Models**
- **fix: Correct line numbers in templ error messages for
arch_templ.go**
- **refactor: use common types for peer roles**
- **refactor: move common types and ORM to a shared package**
- **fix: Config import dwn**
- **refactor: move nebula directory to app**
- **feat: Rebuild nebula**
- **fix: correct file paths in panels templates**
- **feat: Remove duplicate types**
- **refactor: Move dwn to pkg/core**
- **refactor: Binary Structure**
- **feat: Introduce Crypto Pkg**
- **fix: Broken Process Start**
- **feat: Update pkg/* structure**
- **feat: Refactor PKL Structure**
- **build: update pkl build process**
- **chore: Remove Empty Files**
- **refactor: remove unused macaroon package**
- **feat: Add WebAwesome Components**
- **refactor: consolidate build and generation tasks into a single
taskfile, remove redundant makefile targets**
- **refactor: refactor server and move components to pkg/core/dwn**
- **build: update go modules**
- **refactor: move gateway logic into dedicated hway command**
- **feat: Add KSS (Krawczyk-Song-Song) MPC cryptography module**
- **feat: Implement MPC-based JWT signing and UCAN token generation**
- **feat: add support for MPC-based JWT signing**
- **feat: Implement MPC-based UCAN capabilities for smart accounts**
- **feat: add address field to keyshareSource**
- **feat: Add comprehensive MPC test suite for keyshares, UCAN tokens,
and token attenuations**
- **refactor: improve MPC keyshare management and signing process**
- **feat: enhance MPC capability hierarchy documentation**
- **refactor: rename GenerateKeyshares function to NewKeyshareSource for
clarity**
- **refactor: remove unused Ethereum address computation**
- **feat: Add HasHandle and IsAuthenticated methods to HTTPContext**
- **refactor: Add context.Context support to session HTTPContext**
- **refactor: Resolve context interface conflicts in HTTPContext**
- **feat: Add session ID context key and helper functions**
- **feat: Update WebApp Page Rendering**
- **refactor: Simplify context management by using single HTTPContext
key**
- **refactor: Simplify HTTPContext creation and context management in
session middleware**
- **refactor: refactor session middleware to use a single data
structure**
- **refactor: Simplify HTTPContext implementation and session data
handling**
- **refactor: Improve session context handling and prevent nil pointer
errors**
- **refactor: Improve session context handling with nil safety and type
support**
- **refactor: improve session data injection**
- **feat: add full-screen modal component and update registration flow**
- **chore: add .air.toml to .gitignore**
- **feat: add Air to devbox and update dependencies**
This commit is contained in:
Prad Nukala
2024-11-23 01:28:58 -05:00
committed by GitHub
parent bf94277b0f
commit 89989fa102
549 changed files with 74162 additions and 9856 deletions
+208
View File
@@ -0,0 +1,208 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
// Package bls_sig is an implementation of the BLS signature defined in https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-03
package bls_sig
import (
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"fmt"
"golang.org/x/crypto/hkdf"
"github.com/onsonr/sonr/pkg/crypto/core/curves"
"github.com/onsonr/sonr/pkg/crypto/core/curves/native"
"github.com/onsonr/sonr/pkg/crypto/core/curves/native/bls12381"
"github.com/onsonr/sonr/pkg/crypto/internal"
"github.com/onsonr/sonr/pkg/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
}
+284
View File
@@ -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/sonr/pkg/crypto/core/curves/native/bls12381"
"github.com/onsonr/sonr/pkg/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)
}
}
}
+438
View File
@@ -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
View File
@@ -0,0 +1,489 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"fmt"
"github.com/onsonr/sonr/pkg/crypto/core/curves/native"
"github.com/onsonr/sonr/pkg/crypto/core/curves/native/bls12381"
"github.com/onsonr/sonr/pkg/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
View File
@@ -0,0 +1,381 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"testing"
"github.com/onsonr/sonr/pkg/crypto/core/curves/native/bls12381"
)
func generateAugSignatureG1(sk *SecretKey, msg []byte, t *testing.T) *SignatureVt {
bls := NewSigAugVt()
sig, err := bls.Sign(sk, msg)
if err != nil {
t.Errorf("Aug Sign failed")
}
return sig
}
func generateAugAggregateDataG1(t *testing.T) ([]*PublicKeyVt, []*SignatureVt, [][]byte) {
msgs := make([][]byte, numAggregateG1)
pks := make([]*PublicKeyVt, numAggregateG1)
sigs := make([]*SignatureVt, numAggregateG1)
ikm := make([]byte, 32)
bls := NewSigAugVt()
for i := 0; i < numAggregateG1; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
msg := make([]byte, 20)
readRand(msg, t)
sig := generateAugSignatureG1(sk, msg, t)
msgs[i] = msg
sigs[i] = sig
pks[i] = pk
}
return pks, sigs, msgs
}
func TestAugKeyGenG1Works(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAugVt()
_, _, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
}
func TestAugKeyGenG1Fail(t *testing.T) {
ikm := make([]byte, 10)
readRand(ikm, t)
bls := NewSigAugVt()
_, _, err := bls.KeygenWithSeed(ikm)
if err == nil {
t.Errorf("Aug KeyGen succeeded when it should've failed")
}
}
func TestAugCustomDstG1(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAugVtWithDst("BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_AUG_TEST")
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug Custom Dst KeyGen failed")
}
readRand(ikm, t)
sig, err := bls.Sign(sk, ikm)
if err != nil {
t.Errorf("Aug Custom Dst Sign failed")
}
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Aug Custom Dst Verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Aug Custom Dst Verify succeeded when it should've failed.")
}
}
func TestAugSigningG1(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAugVt()
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
readRand(ikm, t)
sig := generateAugSignatureG1(sk, ikm, t)
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Aug Verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Aug Verify succeeded when it should've failed.")
}
}
func TestAugAggregateVerifyG1Works(t *testing.T) {
pks, sigs, msgs := generateAugAggregateDataG1(t)
bls := NewSigAugVt()
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug AggregateVerify failed")
}
}
func TestAugAggregateVerifyG1BadPks(t *testing.T) {
bls := NewSigAugVt()
pks, sigs, msgs := generateAugAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug AggregateVerify failed")
}
pks[0] = pks[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug AggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
pkValue := new(bls12381.G2).Identity()
pks[0] = &PublicKeyVt{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug AggregateVerify succeeded with zero byte public key it should've failed")
}
// Try with base generator
pkValue.Generator()
pks[0] = &PublicKeyVt{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded with the base generator public key it should've failed")
}
}
func TestAugAggregateVerifyG1BadSigs(t *testing.T) {
bls := NewSigAugVt()
pks, sigs, msgs := generateAugAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug aggregateVerify failed")
}
sigs[0] = sigs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
// Try a zero signature to make sure it doesn't crash
sigValue := new(bls12381.G1).Identity()
sigs[0] = &SignatureVt{value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded with zero byte signature it should've failed")
}
// Try with base generator
sigValue.Generator()
sigs[0] = &SignatureVt{value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded with the base generator signature it should've failed")
}
}
func TestAugAggregateVerifyG1BadMsgs(t *testing.T) {
bls := NewSigAugVt()
pks, sigs, msgs := generateAugAggregateDataG1(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug aggregateVerify failed")
}
// Test len(pks) != len(msgs)
if res, _ := bls.AggregateVerify(pks, msgs[0:8], sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
msgs[0] = msgs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
}
func TestAugAggregateVerifyG1DupMsg(t *testing.T) {
bls := NewSigAugVt()
// Only two messages but repeated
messages := make([][]byte, numAggregateG1)
messages[0] = []byte("Yes")
messages[1] = []byte("No")
for i := 2; i < numAggregateG1; i++ {
messages[i] = messages[i%2]
}
pks := make([]*PublicKeyVt, numAggregateG1)
sigs := make([]*SignatureVt, numAggregateG1)
ikm := make([]byte, 32)
for i := 0; i < numAggregateG1; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
sig := generateAugSignatureG1(sk, messages[i], t)
pks[i] = pk
sigs[i] = sig
}
if res, _ := bls.AggregateVerify(pks, messages, sigs); !res {
t.Errorf("Aug aggregateVerify failed for duplicate messages")
}
}
func TestBlsAugG1KeyGen(t *testing.T) {
bls := NewSigAugVt()
_, _, err := bls.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
}
func TestAugVtThresholdKeygenBadInputs(t *testing.T) {
bls := NewSigAugVt()
_, _, err := bls.ThresholdKeygen(0, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(1, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(3, 2)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
}
func TestAugVtThresholdKeygen(t *testing.T) {
bls := NewSigAugVt()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
if len(sks) != 5 {
t.Errorf("ThresholdKeygen did not produce enough shares")
}
}
func TestAugPartialSignVt(t *testing.T) {
ikm := make([]byte, 32)
bls := NewSigAugVt()
pk, sks, err := bls.ThresholdKeygenWithSeed(ikm, 2, 4)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
msg := make([]byte, 10)
sig1, err := bls.PartialSign(sks[0], pk, msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig2, err := bls.PartialSign(sks[1], pk, msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig, err := bls.CombineSignatures(sig1, sig2)
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("Combined signature does not verify")
}
sig, err = bls.CombineSignatures(sig1)
if err == nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); res {
t.Errorf("Combined signature verify succeeded when it should've failed")
}
}
// Ensure that mixed partial signatures from distinct origins create invalid composite signatures
func TestAugVtPartialMixupShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigAugVt()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
for i := range ikm {
ikm[i] = 1
}
pk2, sks2, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignatureVt, total)
sigs2 := make([]*PartialSignatureVt, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], pk1, msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
sigs2[i], err = bls.PartialSign(sks2[i], pk2, msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining 2 from group 1 and 2 from group 2
sig, err := bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[2], sigs2[3])
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf("CombineSignatures worked with different shares of two secret keys for the same message")
}
if res, _ := bls.Verify(pk2, msg, sig); res {
t.Errorf("CombineSignatures worked with different shares of two secret keys for the same message")
}
// Should error out due to duplicate identifiers
_, err = bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[0], sigs2[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded.")
}
}
func TestEmptyMessageAugPartialSignVt(t *testing.T) {
bls := NewSigAugVt()
pk, sks, err := bls.ThresholdKeygen(5, 6)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
// Sign an empty message
_, err = bls.PartialSign(sks[0], pk, []byte{})
if err == nil {
t.Errorf("Expected partial sign to fail on empty message")
}
}
func TestNilMessageAugPartialSignVt(t *testing.T) {
bls := NewSigAugVt()
pk, sks, err := bls.ThresholdKeygen(5, 6)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
// Sign nil message
_, err = bls.PartialSign(sks[0], pk, nil)
if err == nil {
t.Errorf("Expected partial signing to fail on nil message")
}
}
@@ -0,0 +1,377 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"testing"
"github.com/onsonr/sonr/pkg/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
View File
@@ -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/sonr/pkg/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.")
}
}
+446
View File
@@ -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
View File
@@ -0,0 +1,491 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"fmt"
"github.com/onsonr/sonr/pkg/crypto/core/curves/native"
"github.com/onsonr/sonr/pkg/crypto/core/curves/native/bls12381"
"github.com/onsonr/sonr/pkg/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
View File
@@ -0,0 +1,409 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"testing"
"github.com/onsonr/sonr/pkg/crypto/core/curves/native/bls12381"
)
func generateAugSignatureG2(sk *SecretKey, msg []byte, t *testing.T) *Signature {
bls := NewSigAug()
sig, err := bls.Sign(sk, msg)
if err != nil {
t.Errorf("Aug Sign failed")
}
return sig
}
func generateAugAggregateDataG2(t *testing.T) ([]*PublicKey, []*Signature, [][]byte) {
msgs := make([][]byte, numAggregateG2)
pks := make([]*PublicKey, numAggregateG2)
sigs := make([]*Signature, numAggregateG2)
ikm := make([]byte, 32)
bls := NewSigAug()
for i := 0; i < numAggregateG2; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
msg := make([]byte, 20)
readRand(msg, t)
sig := generateAugSignatureG2(sk, msg, t)
msgs[i] = msg
sigs[i] = sig
pks[i] = pk
}
return pks, sigs, msgs
}
func TestAugKeyGenG2Works(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAug()
_, _, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
}
func TestAugKeyGenG2Fail(t *testing.T) {
ikm := make([]byte, 10)
readRand(ikm, t)
bls := NewSigAug()
_, _, err := bls.KeygenWithSeed(ikm)
if err == nil {
t.Errorf("Aug KeyGen succeeded when it should've failed")
}
}
func TestAugCustomDstG2(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAugWithDst("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_TEST")
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug Custom Dst KeyGen failed")
}
readRand(ikm, t)
sig, err := bls.Sign(sk, ikm)
if err != nil {
t.Errorf("Aug Custom Dst Sign failed")
}
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Aug Custon Dst Verify failed")
}
ikm[0] = 0
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Aug Custom Dst Verify succeeded when it should've failed.")
}
}
func TestAugSigningG2(t *testing.T) {
ikm := make([]byte, 32)
readRand(ikm, t)
bls := NewSigAug()
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
readRand(ikm, t)
sig := generateAugSignatureG2(sk, ikm, t)
if res, _ := bls.Verify(pk, ikm, sig); !res {
t.Errorf("Aug Verify failed")
}
ikm[0] += 1
if res, _ := bls.Verify(pk, ikm, sig); res {
t.Errorf("Aug Verify succeeded when it should've failed.")
}
}
func TestAugSignEmptyMessage(t *testing.T) {
bls := NewSigAug()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Aug KeyGen failed")
}
// Sign a nil message
_, err = bls.Sign(sk, nil)
if err == nil {
t.Errorf("Expected sign of nil message to fail")
}
}
func TestAugSignNilMessage(t *testing.T) {
bls := NewSigAug()
_, sk, err := bls.Keygen()
if err != nil {
t.Errorf("Aug KeyGen failed")
}
// Sign an empty message
_, err = bls.Sign(sk, []byte{})
if err == nil {
t.Errorf("Expected sign of empty message to fail")
}
}
func TestAugAggregateVerifyG2Works(t *testing.T) {
pks, sigs, msgs := generateAugAggregateDataG2(t)
bls := NewSigAug()
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug AggregateVerify failed")
}
}
func TestAugAggregateVerifyG2BadPks(t *testing.T) {
bls := NewSigAug()
pks, sigs, msgs := generateAugAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug AggregateVerify failed")
}
pks[0] = pks[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug AggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
pkValue := new(bls12381.G1).Identity()
pks[0] = &PublicKey{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug AggregateVerify succeeded with zero byte public key it should've failed")
}
// Try with base generator
pkValue.Generator()
pks[0] = &PublicKey{value: *pkValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded with the base generator public key it should've failed")
}
}
func TestAugAggregateVerifyG2BadSigs(t *testing.T) {
bls := NewSigAug()
pks, sigs, msgs := generateAugAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug aggregateVerify failed")
}
sigs[0] = sigs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
// Try a zero key to make sure it doesn't crash
sigValue := new(bls12381.G2).Identity()
sigs[0] = &Signature{Value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded with zero byte signature it should've failed")
}
// Try with base generator
sigValue.Generator()
sigs[0] = &Signature{Value: *sigValue}
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded with the base generator signature it should've failed")
}
}
func TestAugAggregateVerifyG2BadMsgs(t *testing.T) {
bls := NewSigAug()
pks, sigs, msgs := generateAugAggregateDataG2(t)
if res, _ := bls.AggregateVerify(pks, msgs, sigs); !res {
t.Errorf("Aug aggregateVerify failed")
}
// Test len(pks) != len(msgs)
if res, _ := bls.AggregateVerify(pks, msgs[0:8], sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
msgs[0] = msgs[1]
if res, _ := bls.AggregateVerify(pks, msgs, sigs); res {
t.Errorf("Aug aggregateVerify succeeded when it should've failed")
}
}
func TestAugAggregateVerifyG2DupMsg(t *testing.T) {
bls := NewSigAug()
// Only two messages but repeated
messages := make([][]byte, numAggregateG2)
messages[0] = []byte("Yes")
messages[1] = []byte("No")
for i := 2; i < numAggregateG2; i++ {
messages[i] = messages[i%2]
}
pks := make([]*PublicKey, numAggregateG2)
sigs := make([]*Signature, numAggregateG2)
ikm := make([]byte, 32)
for i := 0; i < numAggregateG2; i++ {
readRand(ikm, t)
pk, sk, err := bls.KeygenWithSeed(ikm)
if err != nil {
t.Errorf("Aug KeyGen failed")
}
sig := generateAugSignatureG2(sk, messages[i], t)
pks[i] = pk
sigs[i] = sig
}
if res, _ := bls.AggregateVerify(pks, messages, sigs); !res {
t.Errorf("Aug aggregateVerify failed for duplicate messages")
}
}
func TestBlsAugG2KeyGen(t *testing.T) {
bls := NewSigAug()
_, _, err := bls.Keygen()
if err != nil {
t.Errorf("Keygen failed: %v", err)
}
}
func TestAugThresholdKeygenBadInputs(t *testing.T) {
bls := NewSigAug()
_, _, err := bls.ThresholdKeygen(0, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(1, 0)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
_, _, err = bls.ThresholdKeygen(3, 2)
if err == nil {
t.Errorf("ThresholdKeygen should've failed but succeeded")
}
}
func TestAugThresholdKeygen(t *testing.T) {
bls := NewSigAug()
_, sks, err := bls.ThresholdKeygen(3, 5)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
if len(sks) != 5 {
t.Errorf("ThresholdKeygen did not produce enough shares")
}
}
func TestAugPartialSign(t *testing.T) {
ikm := make([]byte, 32)
bls := NewSigAug()
pk, sks, err := bls.ThresholdKeygenWithSeed(ikm, 2, 4)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
msg := make([]byte, 10)
sig1, err := bls.PartialSign(sks[0], pk, msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig2, err := bls.PartialSign(sks[1], pk, msg)
if err != nil {
t.Errorf("partialSign failed: %v", err)
}
sig, err := bls.CombineSignatures(sig1, sig2)
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
if res, _ := bls.Verify(pk, msg, sig); !res {
t.Errorf("Combined signature does not verify")
}
sig, err = bls.CombineSignatures(sig1)
if err == nil {
t.Errorf("CombineSignatures succeeded when it should've failed")
}
if res, _ := bls.Verify(pk, msg, sig); res {
t.Errorf("Combined signature verify succeeded when it should've failed")
}
}
// Ensure that mixed partial signatures from distinct origins create invalid composite signatures
func TestAugPartialMixupShares(t *testing.T) {
total := uint(5)
ikm := make([]byte, 32)
bls := NewSigAug()
pk1, sks1, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
for i := range ikm {
ikm[i] = 1
}
pk2, sks2, err := bls.ThresholdKeygenWithSeed(ikm, 3, total)
if err != nil {
t.Errorf("ThresholdKeygen failed: %v", err)
}
// Generate partial signatures for both sets of keys
msg := make([]byte, 10)
sigs1 := make([]*PartialSignature, total)
sigs2 := make([]*PartialSignature, total)
for i := range sks1 {
sigs1[i], err = bls.PartialSign(sks1[i], pk1, msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
sigs2[i], err = bls.PartialSign(sks2[i], pk2, msg)
if err != nil {
t.Errorf("PartialSign failed: %v", err)
}
}
// Try combining 2 from group 1 and 2 from group 2
sig, err := bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[2], sigs2[3])
if err != nil {
t.Errorf("CombineSignatures failed: %v", err)
}
// Signature shouldn't validate
if res, _ := bls.Verify(pk1, msg, sig); res {
t.Errorf("CombineSignatures worked with different shares of two secret keys for the same message")
}
if res, _ := bls.Verify(pk2, msg, sig); res {
t.Errorf("CombineSignatures worked with different shares of two secret keys for the same message")
}
// Should error out due to duplicate identifiers
_, err = bls.CombineSignatures(sigs1[0], sigs1[1], sigs2[0], sigs2[1])
if err == nil {
t.Errorf("CombineSignatures expected to fail but succeeded.")
}
}
func TestAugPartialSignEmptyMessage(t *testing.T) {
bls := NewSigAug()
pk, sks, err := bls.ThresholdKeygen(2, 2)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
// Test signing an empty message
_, err = bls.PartialSign(sks[0], pk, []byte{})
if err == nil {
t.Errorf("Expected partial sign of empty message to fail")
}
}
func TestAugPartialSignNilMessage(t *testing.T) {
bls := NewSigAug()
pk, sks, err := bls.ThresholdKeygen(7, 7)
if err != nil {
t.Errorf("ThresholdKeygen failed")
}
// Test signing a nil message
_, err = bls.PartialSign(sks[0], pk, nil)
if err == nil {
t.Errorf("Expected partial sign of nil message to fail")
}
}
@@ -0,0 +1,409 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls_sig
import (
"testing"
"github.com/onsonr/sonr/pkg/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
View File
@@ -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/sonr/pkg/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)
}
}