github.com/onflow/flow-go/crypto@v0.24.8/bls_thresholdsign_test.go (about)

     1  //go:build relic
     2  // +build relic
     3  
     4  package crypto
     5  
     6  import (
     7  	crand "crypto/rand"
     8  	"fmt"
     9  	"sync"
    10  	"testing"
    11  	"time"
    12  
    13  	log "github.com/sirupsen/logrus"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestBLSThresholdSignature(t *testing.T) {
    19  	// stateless API
    20  	t.Run("centralized_stateless_keygen", testCentralizedStatelessAPI)
    21  	// stateful API
    22  	t.Run("centralized_stateful_keygen", testCentralizedStatefulAPI)
    23  	t.Run("distributed_stateful_feldmanVSS_keygen", testDistributedStatefulAPI_FeldmanVSS)
    24  	t.Run("distributed_stateful_jointFeldman_keygen", testDistributedStatefulAPI_JointFeldman) // Flow Random beacon case
    25  }
    26  
    27  const thresholdSignatureTag = "random tag"
    28  
    29  var thresholdSignatureMessage = []byte("random message")
    30  
    31  // centralized test of the stateful threshold signature using the threshold key generation.
    32  func testCentralizedStatefulAPI(t *testing.T) {
    33  	n := 10
    34  	for threshold := MinimumThreshold; threshold < n; threshold++ {
    35  		// generate threshold keys
    36  		rand := getPRG(t)
    37  		seed := make([]byte, SeedMinLenDKG)
    38  		_, err := rand.Read(seed)
    39  		require.NoError(t, err)
    40  		skShares, pkShares, pkGroup, err := BLSThresholdKeyGen(n, threshold, seed)
    41  		require.NoError(t, err)
    42  		// generate signature shares
    43  		signers := make([]int, 0, n)
    44  		// hasher
    45  		kmac := NewExpandMsgXOFKMAC128(thresholdSignatureTag)
    46  		// fill the signers list and shuffle it
    47  		for i := 0; i < n; i++ {
    48  			signers = append(signers, i)
    49  		}
    50  		rand.Shuffle(n, func(i, j int) {
    51  			signers[i], signers[j] = signers[j], signers[i]
    52  		})
    53  
    54  		t.Run("happy path", func(t *testing.T) {
    55  			// create the stateful threshold signer
    56  			ts, err := NewBLSThresholdSignatureInspector(pkGroup, pkShares, threshold, thresholdSignatureMessage, thresholdSignatureTag)
    57  			require.NoError(t, err)
    58  
    59  			// check EnoughShares
    60  			enough := ts.EnoughShares()
    61  			assert.False(t, enough)
    62  			var wg sync.WaitGroup
    63  			// create (t) signatures of the first randomly chosen signers
    64  			// ( 1 signature short of the threshold)
    65  			for j := 0; j < threshold; j++ {
    66  				wg.Add(1)
    67  				// test thread safety
    68  				go func(j int) {
    69  					defer wg.Done()
    70  					i := signers[j]
    71  					share, err := skShares[i].Sign(thresholdSignatureMessage, kmac)
    72  					require.NoError(t, err)
    73  					// VerifyShare
    74  					verif, err := ts.VerifyShare(i, share)
    75  					assert.NoError(t, err)
    76  					assert.True(t, verif, "signature should be valid")
    77  					// check HasSignature is false
    78  					ok, err := ts.HasShare(i)
    79  					assert.NoError(t, err)
    80  					assert.False(t, ok)
    81  					// TrustedAdd
    82  					enough, err := ts.TrustedAdd(i, share)
    83  					assert.NoError(t, err)
    84  					assert.False(t, enough)
    85  					// check HasShare is true
    86  					ok, err = ts.HasShare(i)
    87  					assert.NoError(t, err)
    88  					assert.True(t, ok)
    89  					// check EnoughSignature
    90  					assert.False(t, ts.EnoughShares(), "threshold shouldn't be reached")
    91  					// check ThresholdSignature
    92  					sig, err := ts.ThresholdSignature()
    93  					assert.Error(t, err)
    94  					assert.True(t, IsNotEnoughSharesError(err))
    95  					assert.Nil(t, sig)
    96  				}(j)
    97  			}
    98  			wg.Wait()
    99  			// add the last required signature to get (t+1) shares
   100  			i := signers[threshold]
   101  			share, err := skShares[i].Sign(thresholdSignatureMessage, kmac)
   102  			require.NoError(t, err)
   103  			verif, enough, err := ts.VerifyAndAdd(i, share)
   104  			assert.NoError(t, err)
   105  			assert.True(t, verif)
   106  			assert.True(t, enough)
   107  			// check EnoughSignature
   108  			assert.True(t, ts.EnoughShares())
   109  
   110  			// add a share when threshold is reached
   111  			if threshold+1 < n {
   112  				i := signers[threshold+1]
   113  				share, err := skShares[i].Sign(thresholdSignatureMessage, kmac)
   114  				require.NoError(t, err)
   115  				// Trusted Add
   116  				enough, err := ts.TrustedAdd(i, share)
   117  				assert.NoError(t, err)
   118  				assert.True(t, enough)
   119  				// VerifyAndAdd
   120  				verif, enough, err := ts.VerifyAndAdd(i, share)
   121  				assert.NoError(t, err)
   122  				assert.True(t, verif)
   123  				assert.True(t, enough)
   124  			}
   125  			// reconstruct the threshold signature
   126  			thresholdsignature, err := ts.ThresholdSignature()
   127  			require.NoError(t, err)
   128  			// VerifyThresholdSignature
   129  			verif, err = ts.VerifyThresholdSignature(thresholdsignature)
   130  			require.NoError(t, err)
   131  			assert.True(t, verif)
   132  		})
   133  
   134  		t.Run("duplicate signer", func(t *testing.T) {
   135  			// create the stateful threshold signer
   136  			ts, err := NewBLSThresholdSignatureInspector(pkGroup, pkShares, threshold, thresholdSignatureMessage, thresholdSignatureTag)
   137  			require.NoError(t, err)
   138  
   139  			// Create a share and add it
   140  			i := rand.Intn(n)
   141  			share, err := skShares[i].Sign(thresholdSignatureMessage, kmac)
   142  			require.NoError(t, err)
   143  			enough, err := ts.TrustedAdd(i, share)
   144  			assert.NoError(t, err)
   145  			assert.False(t, enough)
   146  
   147  			// Add an existing share
   148  
   149  			// VerifyAndAdd
   150  			verif, enough, err := ts.VerifyAndAdd(i, share)
   151  			assert.Error(t, err)
   152  			assert.True(t, IsDuplicatedSignerError(err))
   153  			assert.False(t, verif)
   154  			assert.False(t, enough)
   155  			// TrustedAdd
   156  			enough, err = ts.TrustedAdd(i, share)
   157  			assert.Error(t, err)
   158  			assert.True(t, IsDuplicatedSignerError(err))
   159  			assert.False(t, enough)
   160  		})
   161  
   162  		t.Run("Invalid index", func(t *testing.T) {
   163  			// create the stateful threshold signer
   164  			ts, err := NewBLSThresholdSignatureInspector(pkGroup, pkShares, threshold, thresholdSignatureMessage, thresholdSignatureTag)
   165  			require.NoError(t, err)
   166  
   167  			share, err := skShares[0].Sign(thresholdSignatureMessage, kmac)
   168  			require.NoError(t, err)
   169  			// invalid index
   170  			invalidIndex := len(pkShares) + 1
   171  			// VerifyShare
   172  			verif, err := ts.VerifyShare(invalidIndex, share)
   173  			assert.Error(t, err)
   174  			assert.True(t, IsInvalidInputsError(err))
   175  			assert.False(t, verif)
   176  			// TrustedAdd
   177  			enough, err := ts.TrustedAdd(invalidIndex, share)
   178  			assert.Error(t, err)
   179  			assert.True(t, IsInvalidInputsError(err))
   180  			assert.False(t, enough)
   181  			// VerifyAndAdd
   182  			verif, enough, err = ts.VerifyAndAdd(invalidIndex, share)
   183  			assert.Error(t, err)
   184  			assert.True(t, IsInvalidInputsError(err))
   185  			assert.False(t, verif)
   186  			assert.False(t, enough)
   187  			// HasShare
   188  			verif, err = ts.HasShare(invalidIndex)
   189  			assert.Error(t, err)
   190  			assert.True(t, IsInvalidInputsError(err))
   191  			assert.False(t, verif)
   192  		})
   193  
   194  		t.Run("invalid signature", func(t *testing.T) {
   195  			index := signers[0]
   196  			ts, err := NewBLSThresholdSignatureInspector(pkGroup, pkShares, threshold, thresholdSignatureMessage, thresholdSignatureTag)
   197  			require.NoError(t, err)
   198  			share, err := skShares[index].Sign(thresholdSignatureMessage, kmac)
   199  			require.NoError(t, err)
   200  
   201  			// alter signature - invalid serialization
   202  			tmp := share[0]
   203  			share[0] = invalidBLSSignatureHeader
   204  			// VerifyShare
   205  			verif, err := ts.VerifyShare(index, share)
   206  			assert.NoError(t, err)
   207  			assert.False(t, verif)
   208  			// VerifyAndAdd
   209  			verif, enough, err := ts.VerifyAndAdd(index, share)
   210  			assert.NoError(t, err)
   211  			assert.False(t, verif)
   212  			assert.False(t, enough)
   213  			// check share was not added
   214  			verif, err = ts.HasShare(index)
   215  			assert.NoError(t, err)
   216  			assert.False(t, verif)
   217  			// restore share
   218  			share[0] = tmp
   219  
   220  			// valid curve point but invalid signature
   221  			otherIndex := (index + 1) % n // otherIndex is different than index
   222  			// VerifyShare
   223  			verif, err = ts.VerifyShare(otherIndex, share)
   224  			assert.NoError(t, err)
   225  			assert.False(t, verif)
   226  			// VerifyAndAdd
   227  			verif, enough, err = ts.VerifyAndAdd(otherIndex, share)
   228  			assert.NoError(t, err)
   229  			assert.False(t, verif)
   230  			assert.False(t, enough)
   231  			// check share was not added
   232  			verif, err = ts.HasShare(otherIndex)
   233  			assert.NoError(t, err)
   234  			assert.False(t, verif)
   235  
   236  			// trust add one invalid signature and check ThresholdSignature
   237  			tmp = share[0]
   238  			share[0] = invalidBLSSignatureHeader      // alter the share
   239  			enough, err = ts.TrustedAdd(index, share) // invalid share
   240  			assert.NoError(t, err)
   241  			assert.False(t, enough)
   242  			for i := 1; i < threshold+1; i++ { // valid shares
   243  				index := signers[i]
   244  				valid, err := skShares[index].Sign(thresholdSignatureMessage, kmac)
   245  				require.NoError(t, err)
   246  				enough, err = ts.TrustedAdd(index, valid)
   247  				assert.NoError(t, err)
   248  				if i < threshold {
   249  					assert.False(t, enough)
   250  				} else {
   251  					assert.True(t, enough)
   252  				}
   253  			}
   254  			sig, err := ts.ThresholdSignature()
   255  			assert.Error(t, err)
   256  			assert.True(t, IsInvalidSignatureError(err))
   257  			assert.Nil(t, sig)
   258  			share[0] = tmp // restore the share
   259  		})
   260  
   261  		t.Run("constructor errors", func(t *testing.T) {
   262  			// invalid keys size
   263  			index := rand.Intn(n)
   264  			pkSharesInvalid := make([]PublicKey, ThresholdSignMaxSize+1)
   265  			tsFollower, err := NewBLSThresholdSignatureInspector(pkGroup, pkSharesInvalid, threshold, thresholdSignatureMessage, thresholdSignatureTag)
   266  			assert.Error(t, err)
   267  			assert.True(t, IsInvalidInputsError(err))
   268  			assert.Nil(t, tsFollower)
   269  			// non BLS key share
   270  			seed := make([]byte, KeyGenSeedMinLen)
   271  			_, err = rand.Read(seed)
   272  			require.NoError(t, err)
   273  			skEcdsa, err := GeneratePrivateKey(ECDSAP256, seed)
   274  			require.NoError(t, err)
   275  			tmp := pkShares[0]
   276  			pkShares[0] = skEcdsa.PublicKey()
   277  			tsFollower, err = NewBLSThresholdSignatureInspector(pkGroup, pkShares, threshold, thresholdSignatureMessage, thresholdSignatureTag)
   278  			assert.Error(t, err)
   279  			assert.True(t, IsNotBLSKeyError(err))
   280  			assert.Nil(t, tsFollower)
   281  			pkShares[0] = tmp // restore valid keys
   282  			// non BLS group key
   283  			tsFollower, err = NewBLSThresholdSignatureInspector(skEcdsa.PublicKey(), pkShares, threshold, thresholdSignatureMessage, thresholdSignatureTag)
   284  			assert.Error(t, err)
   285  			assert.True(t, IsNotBLSKeyError(err))
   286  			assert.Nil(t, tsFollower)
   287  			// non BLS private key
   288  			tsParticipant, err := NewBLSThresholdSignatureParticipant(pkGroup, pkShares, threshold, index, skEcdsa, thresholdSignatureMessage, thresholdSignatureTag)
   289  			assert.Error(t, err)
   290  			assert.True(t, IsNotBLSKeyError(err))
   291  			assert.Nil(t, tsParticipant)
   292  			// invalid current index
   293  			tsParticipant, err = NewBLSThresholdSignatureParticipant(pkGroup, pkShares, threshold, len(pkShares)+1, skShares[index], thresholdSignatureMessage, thresholdSignatureTag)
   294  			assert.Error(t, err)
   295  			assert.True(t, IsInvalidInputsError(err))
   296  			assert.Nil(t, tsParticipant)
   297  			// invalid threshold
   298  			tsFollower, err = NewBLSThresholdSignatureInspector(pkGroup, pkShares, len(pkShares)+1, thresholdSignatureMessage, thresholdSignatureTag)
   299  			assert.Error(t, err)
   300  			assert.True(t, IsInvalidInputsError(err))
   301  			assert.Nil(t, tsFollower)
   302  			// inconsistent private and public key
   303  			indexSwap := (index + 1) % n // indexSwap is different than index
   304  			pkShares[index], pkShares[indexSwap] = pkShares[indexSwap], pkShares[index]
   305  			tsParticipant, err = NewBLSThresholdSignatureParticipant(pkGroup, pkShares, len(pkShares)+1, index, skShares[index], thresholdSignatureMessage, thresholdSignatureTag)
   306  			assert.Error(t, err)
   307  			assert.True(t, IsInvalidInputsError(err))
   308  			assert.Nil(t, tsParticipant)
   309  			pkShares[index], pkShares[indexSwap] = pkShares[indexSwap], pkShares[index] // restore keys
   310  		})
   311  	}
   312  }
   313  
   314  // Distributed Threshold Signature stateful api test
   315  // keys are generated using simple Feldman VSS
   316  func testDistributedStatefulAPI_FeldmanVSS(t *testing.T) {
   317  	log.SetLevel(log.ErrorLevel)
   318  	log.Info("DKG starts")
   319  	gt = t
   320  	rand := getPRG(t)
   321  	// number of participants to test
   322  	n := 5
   323  	lead := rand.Intn(n) // random
   324  	var sync sync.WaitGroup
   325  	chans := make([]chan *message, n)
   326  	processors := make([]testDKGProcessor, 0, n)
   327  
   328  	// create n processors for all participants
   329  	for current := 0; current < n; current++ {
   330  		processors = append(processors, testDKGProcessor{
   331  			current:  current,
   332  			chans:    chans,
   333  			protocol: dkgType,
   334  		})
   335  		// create DKG in all participants
   336  		var err error
   337  		processors[current].dkg, err = NewFeldmanVSS(n, optimalThreshold(n),
   338  			current, &processors[current], lead)
   339  		require.NoError(t, err)
   340  	}
   341  
   342  	// create the participant (buffered) communication channels
   343  	for i := 0; i < n; i++ {
   344  		chans[i] = make(chan *message, 2*n)
   345  	}
   346  	// start DKG in all participants
   347  	seed := make([]byte, SeedMinLenDKG)
   348  	read, err := rand.Read(seed)
   349  	require.Equal(t, read, SeedMinLenDKG)
   350  	require.NoError(t, err)
   351  	sync.Add(n)
   352  	for current := 0; current < n; current++ {
   353  		err := processors[current].dkg.Start(seed)
   354  		require.NoError(t, err)
   355  		go tsDkgRunChan(&processors[current], &sync, t, 2)
   356  	}
   357  
   358  	// synchronize the main thread to end DKG
   359  	sync.Wait()
   360  	for i := 1; i < n; i++ {
   361  		assert.True(t, processors[i].pk.Equals(processors[0].pk), "2 group public keys are mismatching")
   362  	}
   363  
   364  	// Start TS
   365  	log.Info("TS starts")
   366  	sync.Add(n)
   367  	for i := 0; i < n; i++ {
   368  		go tsRunChan(&processors[i], &sync, t)
   369  	}
   370  	// synchronize the main thread to end TS
   371  	sync.Wait()
   372  }
   373  
   374  // Distributed  Threshold Signature stateful api test
   375  // keys are generated using Joint-Feldman
   376  func testDistributedStatefulAPI_JointFeldman(t *testing.T) {
   377  	log.SetLevel(log.ErrorLevel)
   378  	log.Info("DKG starts")
   379  	gt = t
   380  	rand := getPRG(t)
   381  	// number of participants to test
   382  	n := 5
   383  	for threshold := MinimumThreshold; threshold < n; threshold++ {
   384  		var sync sync.WaitGroup
   385  		chans := make([]chan *message, n)
   386  		processors := make([]testDKGProcessor, 0, n)
   387  
   388  		// create n processors for all participants
   389  		for current := 0; current < n; current++ {
   390  			processors = append(processors, testDKGProcessor{
   391  				current:  current,
   392  				chans:    chans,
   393  				protocol: dkgType,
   394  			})
   395  			// create DKG in all participants
   396  			var err error
   397  			processors[current].dkg, err = NewJointFeldman(n,
   398  				optimalThreshold(n), current, &processors[current])
   399  			require.NoError(t, err)
   400  		}
   401  
   402  		// create the participant (buffered) communication channels
   403  		for i := 0; i < n; i++ {
   404  			chans[i] = make(chan *message, 2*n)
   405  		}
   406  		// start DKG in all participants but the
   407  		seed := make([]byte, SeedMinLenDKG)
   408  		read, err := rand.Read(seed)
   409  		require.Equal(t, read, SeedMinLenDKG)
   410  		require.NoError(t, err)
   411  		sync.Add(n)
   412  		for current := 0; current < n; current++ {
   413  			err := processors[current].dkg.Start(seed)
   414  			require.NoError(t, err)
   415  			go tsDkgRunChan(&processors[current], &sync, t, 0)
   416  		}
   417  
   418  		// sync the 2 timeouts at all participants and start the next phase
   419  		for phase := 1; phase <= 2; phase++ {
   420  			sync.Wait()
   421  			sync.Add(n)
   422  			for current := 0; current < n; current++ {
   423  				go tsDkgRunChan(&processors[current], &sync, t, phase)
   424  			}
   425  		}
   426  
   427  		// synchronize the main thread to end DKG
   428  		sync.Wait()
   429  		for i := 1; i < n; i++ {
   430  			assert.True(t, processors[i].pk.Equals(processors[0].pk),
   431  				"2 group public keys are mismatching")
   432  		}
   433  
   434  		// Start TS
   435  		log.Info("TS starts")
   436  		sync.Add(n)
   437  		for current := 0; current < n; current++ {
   438  			go tsRunChan(&processors[current], &sync, t)
   439  		}
   440  		// synchronize the main thread to end TS
   441  		sync.Wait()
   442  	}
   443  }
   444  
   445  // This is a testing function
   446  // It simulates processing incoming messages by a participant during DKG
   447  // It assumes proc.dkg is already running
   448  func tsDkgRunChan(proc *testDKGProcessor,
   449  	sync *sync.WaitGroup, t *testing.T, phase int) {
   450  	for {
   451  		select {
   452  		case newMsg := <-proc.chans[proc.current]:
   453  			log.Debugf("%d Receiving DKG from %d:", proc.current, newMsg.orig)
   454  			if newMsg.channel == private {
   455  				err := proc.dkg.HandlePrivateMsg(newMsg.orig, newMsg.data)
   456  				require.Nil(t, err)
   457  			} else {
   458  				err := proc.dkg.HandleBroadcastMsg(newMsg.orig, newMsg.data)
   459  				require.Nil(t, err)
   460  			}
   461  
   462  		// if timeout, finalize DKG and create the threshold signer
   463  		case <-time.After(200 * time.Millisecond):
   464  			switch phase {
   465  			case 0:
   466  				log.Infof("%d shares phase ended \n", proc.current)
   467  				err := proc.dkg.NextTimeout()
   468  				require.NoError(t, err)
   469  			case 1:
   470  				log.Infof("%d complaints phase ended \n", proc.current)
   471  				err := proc.dkg.NextTimeout()
   472  				require.NoError(t, err)
   473  			case 2:
   474  				log.Infof("%d dkg ended \n", proc.current)
   475  				sk, groupPK, nodesPK, err := proc.dkg.End()
   476  				require.NotNil(t, sk)
   477  				require.NotNil(t, groupPK)
   478  				require.NotNil(t, nodesPK)
   479  				require.Nil(t, err, "End dkg failed: %v\n", err)
   480  				proc.pk = groupPK
   481  				n := proc.dkg.Size()
   482  				proc.ts, err = NewBLSThresholdSignatureParticipant(groupPK, nodesPK, optimalThreshold(n), proc.current, sk, thresholdSignatureMessage, thresholdSignatureTag)
   483  				require.NoError(t, err)
   484  				// needed to test the statless api
   485  				proc.keys = &statelessKeys{sk, groupPK, nodesPK}
   486  			}
   487  			sync.Done()
   488  			return
   489  		}
   490  	}
   491  }
   492  
   493  // This is a testing function using the stateful api
   494  // It simulates processing incoming messages by a participant during TS
   495  func tsRunChan(proc *testDKGProcessor, sync *sync.WaitGroup, t *testing.T) {
   496  	// Sign a share and broadcast it
   497  	sigShare, err := proc.ts.SignShare()
   498  	proc.protocol = tsType
   499  	if err != nil { // not using require.Nil for now
   500  		panic(fmt.Sprintf("%d couldn't sign", proc.current))
   501  	}
   502  	proc.Broadcast(sigShare)
   503  	for {
   504  		select {
   505  		case newMsg := <-proc.chans[proc.current]:
   506  			log.Debugf("%d Receiving TS from %d:", proc.current, newMsg.orig)
   507  			verif, enough, err := proc.ts.VerifyAndAdd(
   508  				newMsg.orig, newMsg.data)
   509  			require.NoError(t, err)
   510  			assert.True(t, verif,
   511  				"the signature share sent from %d to %d is not correct", newMsg.orig,
   512  				proc.current)
   513  			log.Info(enough)
   514  			if enough {
   515  				assert.Equal(t, enough, proc.ts.EnoughShares())
   516  				thresholdSignature, err := proc.ts.ThresholdSignature()
   517  				require.NoError(t, err)
   518  				verif, err = proc.ts.VerifyThresholdSignature(thresholdSignature)
   519  				require.NoError(t, err)
   520  				assert.True(t, verif, "the threshold signature is not correct")
   521  				if verif {
   522  					log.Infof("%d reconstructed a valid signature: %d\n", proc.current,
   523  						thresholdSignature)
   524  				}
   525  			}
   526  
   527  		// if timeout, finalize TS
   528  		case <-time.After(time.Second):
   529  			sync.Done()
   530  			return
   531  		}
   532  	}
   533  }
   534  
   535  // This stucture holds the keys and is needed for the stateless test
   536  type statelessKeys struct {
   537  	// the current participant private key (a DKG output)
   538  	myPrivateKey PrivateKey
   539  	// the group public key (a DKG output)
   540  	groupPublicKey PublicKey
   541  	// the group public key shares (a DKG output)
   542  	publicKeyShares []PublicKey
   543  }
   544  
   545  // Centralized test of threshold signature protocol using the threshold key generation.
   546  func testCentralizedStatelessAPI(t *testing.T) {
   547  	rand := getPRG(t)
   548  	n := 10
   549  	for threshold := MinimumThreshold; threshold < n; threshold++ {
   550  		// generate threshold keys
   551  		seed := make([]byte, SeedMinLenDKG)
   552  		_, err := rand.Read(seed)
   553  		require.NoError(t, err)
   554  		skShares, pkShares, pkGroup, err := BLSThresholdKeyGen(n, threshold, seed)
   555  		require.NoError(t, err)
   556  		// signature hasher
   557  		kmac := NewExpandMsgXOFKMAC128(thresholdSignatureTag)
   558  		// generate signature shares
   559  		signShares := make([]Signature, 0, n)
   560  		signers := make([]int, 0, n)
   561  		// fill the signers list and shuffle it
   562  		for i := 0; i < n; i++ {
   563  			signers = append(signers, i)
   564  		}
   565  		rand.Shuffle(n, func(i, j int) {
   566  			signers[i], signers[j] = signers[j], signers[i]
   567  		})
   568  		// create (t+1) signatures of the first randomly chosen signers
   569  		for j := 0; j < threshold+1; j++ {
   570  			i := signers[j]
   571  			share, err := skShares[i].Sign(thresholdSignatureMessage, kmac)
   572  			require.NoError(t, err)
   573  			verif, err := pkShares[i].Verify(share, thresholdSignatureMessage, kmac)
   574  			require.NoError(t, err)
   575  			assert.True(t, verif, "signature share is not valid")
   576  			if verif {
   577  				signShares = append(signShares, share)
   578  			}
   579  		}
   580  		// reconstruct and test the threshold signature
   581  		thresholdSignature, err := BLSReconstructThresholdSignature(n, threshold, signShares, signers[:threshold+1])
   582  		require.NoError(t, err)
   583  		verif, err := pkGroup.Verify(thresholdSignature, thresholdSignatureMessage, kmac)
   584  		require.NoError(t, err)
   585  		assert.True(t, verif, "signature share is not valid")
   586  
   587  		// check failure with a random redundant signer
   588  		if threshold > 1 {
   589  			randomDuplicate := rand.Intn(int(threshold)) + 1 // 1 <= duplicate <= threshold
   590  			tmp := signers[randomDuplicate]
   591  			signers[randomDuplicate] = signers[0]
   592  			thresholdSignature, err = BLSReconstructThresholdSignature(n, threshold, signShares, signers[:threshold+1])
   593  			assert.Error(t, err)
   594  			assert.True(t, IsDuplicatedSignerError(err))
   595  			assert.Nil(t, thresholdSignature)
   596  			signers[randomDuplicate] = tmp
   597  		}
   598  
   599  		// check with an invalid signature (invalid serialization)
   600  		invalidSig := make([]byte, signatureLengthBLSBLS12381)
   601  		signShares[0] = invalidSig
   602  		thresholdSignature, err = BLSReconstructThresholdSignature(n, threshold, signShares, signers[:threshold+1])
   603  		assert.Error(t, err)
   604  		assert.True(t, IsInvalidSignatureError(err))
   605  		assert.Nil(t, thresholdSignature)
   606  	}
   607  }
   608  
   609  func BenchmarkSimpleKeyGen(b *testing.B) {
   610  	n := 60
   611  	seed := make([]byte, SeedMinLenDKG)
   612  	_, err := crand.Read(seed)
   613  	require.NoError(b, err)
   614  	b.ResetTimer()
   615  	for i := 0; i < b.N; i++ {
   616  		_, _, _, _ = BLSThresholdKeyGen(n, optimalThreshold(n), seed)
   617  	}
   618  	b.StopTimer()
   619  }