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

     1  //go:build relic
     2  // +build relic
     3  
     4  package crypto
     5  
     6  // #cgo CFLAGS: -g -Wall -std=c99
     7  // #include "bls_thresholdsign_include.h"
     8  import "C"
     9  
    10  import (
    11  	"fmt"
    12  	"sync"
    13  
    14  	"github.com/onflow/flow-go/crypto/hash"
    15  )
    16  
    17  // BLS-based threshold signature on BLS 12-381 curve
    18  // The BLS settings are the same as in the signature
    19  // scheme defined in the package.
    20  
    21  // A threshold signature scheme allows any subset of (t+1)
    22  // valid signature shares to reconstruct the threshold signature.
    23  // Up to (t) shares do not reveal any information about the threshold
    24  // signature.
    25  // Although the API allows using arbitrary values of (t),
    26  // the threshold signature scheme is secure in the presence of up to (t)
    27  // malicious participants when (t < n/2).
    28  // In order to optimize equally for unforgeability and robustness,
    29  // the input threshold value (t) should be set to t = floor((n-1)/2).
    30  
    31  // The package offers two api for BLS threshold signature:
    32  // - stateful api where a structure holds all information
    33  //  of the threshold signature protocols and is recommended
    34  //  to be used for safety and to reduce protocol inconsistencies.
    35  // - stateless api with signature reconstruction. Verifying and storing
    36  // the signature shares has to be managed outside of the library.
    37  
    38  // blsThresholdSignatureParticipant implements ThresholdSignatureParticipant
    39  // based on the BLS signature scheme
    40  type blsThresholdSignatureParticipant struct {
    41  	// embed the follower
    42  	*blsThresholdSignatureInspector
    43  	// the index of the current participant
    44  	myIndex int
    45  	// the current participant private key (a threshold KG output)
    46  	myPrivateKey PrivateKey
    47  }
    48  
    49  // blsThresholdSignatureInspector implements ThresholdSignatureInspector
    50  // based on the BLS signature scheme
    51  type blsThresholdSignatureInspector struct {
    52  	// size of the group
    53  	size int
    54  	// the threshold t of the scheme where (t+1) shares are
    55  	// required to reconstruct a signature
    56  	threshold int
    57  	// the group public key (a threshold KG output)
    58  	groupPublicKey PublicKey
    59  	// the group public key shares (a threshold KG output)
    60  	publicKeyShares []PublicKey
    61  	// the hasher to be used for all signatures
    62  	hasher hash.Hasher
    63  	// the message to be signed. Signature shares and the threshold signature
    64  	// are verified against this message
    65  	message []byte
    66  	// the valid signature shares received from other participants
    67  	shares map[index]Signature
    68  	// the threshold signature. It is equal to nil if less than (t+1) shares are
    69  	// received
    70  	thresholdSignature Signature
    71  	// lock for atomic operations
    72  	lock sync.RWMutex
    73  }
    74  
    75  // NewBLSThresholdSignatureParticipant creates a new instance of Threshold signature Participant using BLS.
    76  // A participant is able to participate in a threshold signing protocol as well as following the
    77  // protocol.
    78  //
    79  // A new instance is needed for each set of public keys and message.
    80  // If the key set or message change, a new structure needs to be instantiated.
    81  // Participants are defined by their public key share, and are indexed from 0 to n-1. The current
    82  // participant is indexed by `myIndex` and holds the input private key
    83  // where n is the length of the public key shares slice.
    84  //
    85  // The function returns
    86  //   - (nil, invalidInputsError) if:
    87  //   - n is not in [`ThresholdSignMinSize`, `ThresholdSignMaxSize`]
    88  //   - threshold value is not in interval [1, n-1]
    89  //   - input private key and public key at my index do not match
    90  //   - (nil, notBLSKeyError) if the private or at least one public key is not of type BLS BLS12-381.
    91  //   - (pointer, nil) otherwise
    92  func NewBLSThresholdSignatureParticipant(
    93  	groupPublicKey PublicKey,
    94  	sharePublicKeys []PublicKey,
    95  	threshold int,
    96  	myIndex int,
    97  	myPrivateKey PrivateKey,
    98  	message []byte,
    99  	dsTag string,
   100  ) (*blsThresholdSignatureParticipant, error) {
   101  
   102  	size := len(sharePublicKeys)
   103  	if myIndex >= size || myIndex < 0 {
   104  		return nil, invalidInputsErrorf(
   105  			"the current index must be between 0 and %d, got %d",
   106  			size-1, myIndex)
   107  	}
   108  
   109  	// check private key is BLS key
   110  	if _, ok := myPrivateKey.(*prKeyBLSBLS12381); !ok {
   111  		return nil, fmt.Errorf("private key of participant %d is not valid: %w", myIndex, notBLSKeyError)
   112  	}
   113  
   114  	// create the follower
   115  	follower, err := NewBLSThresholdSignatureInspector(groupPublicKey, sharePublicKeys, threshold, message, dsTag)
   116  	if err != nil {
   117  		return nil, fmt.Errorf("create a threshold signature follower failed: %w", err)
   118  	}
   119  
   120  	// check the private key, index and corresponding public key are consistent
   121  	currentPublicKey := sharePublicKeys[myIndex]
   122  	if !myPrivateKey.PublicKey().Equals(currentPublicKey) {
   123  		return nil, invalidInputsErrorf("private key is not matching public key at index %d", myIndex)
   124  	}
   125  
   126  	return &blsThresholdSignatureParticipant{
   127  		blsThresholdSignatureInspector: follower,
   128  		myIndex:                        myIndex,      // current participant index
   129  		myPrivateKey:                   myPrivateKey, // myPrivateKey is the current participant's own private key share
   130  	}, nil
   131  }
   132  
   133  // NewBLSThresholdSignatureInspector creates a new instance of Threshold signature follower using BLS.
   134  // It only allows following the threshold signing protocol .
   135  //
   136  // A new instance is needed for each set of public keys and message.
   137  // If the key set or message change, a new structure needs to be instantiated.
   138  // Participants are defined by their public key share, and are indexed from 0 to n-1
   139  // where n is the length of the public key shares slice.
   140  //
   141  // The function returns
   142  //   - (nil, invalidInputsError) if:
   143  //   - n is not in [`ThresholdSignMinSize`, `ThresholdSignMaxSize`]
   144  //   - threshold value is not in interval [1, n-1]
   145  //   - (nil, notBLSKeyError) at least one public key is not of type pubKeyBLSBLS12381
   146  //   - (pointer, nil) otherwise
   147  func NewBLSThresholdSignatureInspector(
   148  	groupPublicKey PublicKey,
   149  	sharePublicKeys []PublicKey,
   150  	threshold int,
   151  	message []byte,
   152  	dsTag string,
   153  ) (*blsThresholdSignatureInspector, error) {
   154  
   155  	size := len(sharePublicKeys)
   156  	if size < ThresholdSignMinSize || size > ThresholdSignMaxSize {
   157  		return nil, invalidInputsErrorf(
   158  			"size should be between %d and %d, got %d",
   159  			ThresholdSignMinSize, ThresholdSignMaxSize, size)
   160  	}
   161  	if threshold >= size || threshold < MinimumThreshold {
   162  		return nil, invalidInputsErrorf(
   163  			"the threshold must be between %d and %d, got %d",
   164  			MinimumThreshold, size-1, threshold)
   165  	}
   166  
   167  	// check keys are BLS keys
   168  	for i, pk := range sharePublicKeys {
   169  		if _, ok := pk.(*pubKeyBLSBLS12381); !ok {
   170  			return nil, fmt.Errorf("key at index %d is invalid: %w", i, notBLSKeyError)
   171  		}
   172  	}
   173  	if _, ok := groupPublicKey.(*pubKeyBLSBLS12381); !ok {
   174  		return nil, fmt.Errorf("group key is invalid: %w", notBLSKeyError)
   175  	}
   176  
   177  	return &blsThresholdSignatureInspector{
   178  		size:               size,
   179  		threshold:          threshold,
   180  		message:            message,
   181  		hasher:             NewExpandMsgXOFKMAC128(dsTag),
   182  		shares:             make(map[index]Signature),
   183  		thresholdSignature: nil,
   184  		groupPublicKey:     groupPublicKey,  // groupPublicKey is the group public key corresponding to the group secret key
   185  		publicKeyShares:    sharePublicKeys, // sharePublicKeys are the public key shares corresponding to the private key shares
   186  	}, nil
   187  }
   188  
   189  // SignShare generates a signature share using the current private key share.
   190  //
   191  // The function does not add the share to the internal pool of shares and do
   192  // not update the internal state.
   193  // This function is thread safe and non-blocking
   194  //
   195  // The function returns
   196  //   - (nil, error) if an unexpected error occurs
   197  //   - (signature, nil) otherwise
   198  func (s *blsThresholdSignatureParticipant) SignShare() (Signature, error) {
   199  	share, err := s.myPrivateKey.Sign(s.message, s.hasher)
   200  	if err != nil {
   201  		return nil, fmt.Errorf("share signing failed: %w", err)
   202  	}
   203  	return share, nil
   204  }
   205  
   206  // validIndex returns invalidInputsError error if given index is valid and nil otherwise.
   207  // This function is thread safe.
   208  func (s *blsThresholdSignatureInspector) validIndex(orig int) error {
   209  	if orig >= s.size || orig < 0 {
   210  		return invalidInputsErrorf(
   211  			"origin input is invalid, should be positive less than %d, got %d",
   212  			s.size, orig)
   213  	}
   214  	return nil
   215  }
   216  
   217  // VerifyShare verifies the input signature against the stored message and stored
   218  // key at the input index.
   219  //
   220  // This function does not update the internal state and is thread-safe.
   221  // Returns:
   222  //   - (true, nil) if the signature is valid
   223  //   - (false, nil) if `orig` is valid but the signature share does not verify against
   224  //     the public key share and message.
   225  //   - (false, invalidInputsError) if `orig` is an invalid index value
   226  //   - (false, error) for all other unexpected errors
   227  func (s *blsThresholdSignatureInspector) VerifyShare(orig int, share Signature) (bool, error) {
   228  	// validate index
   229  	if err := s.validIndex(orig); err != nil {
   230  		return false, err
   231  	}
   232  	return s.publicKeyShares[orig].Verify(share, s.message, s.hasher)
   233  }
   234  
   235  // VerifyThresholdSignature verifies the input signature against the stored
   236  // message and stored group public key.
   237  //
   238  // This function does not update the internal state and is thread-safe.
   239  // Returns:
   240  //   - (true, nil) if the signature is valid
   241  //   - (false, nil) if signature is invalid
   242  //   - (false, error) for all other unexpected errors
   243  func (s *blsThresholdSignatureInspector) VerifyThresholdSignature(thresholdSignature Signature) (bool, error) {
   244  	return s.groupPublicKey.Verify(thresholdSignature, s.message, s.hasher)
   245  }
   246  
   247  // EnoughShares indicates whether enough shares have been accumulated in order to reconstruct
   248  // a group signature.
   249  //
   250  // This function is thread safe.
   251  // Returns:
   252  //   - true if and only if at least (threshold+1) shares were added
   253  func (s *blsThresholdSignatureInspector) EnoughShares() bool {
   254  	s.lock.RLock()
   255  	defer s.lock.RUnlock()
   256  
   257  	return s.enoughShares()
   258  }
   259  
   260  // non thread safe version of EnoughShares
   261  func (s *blsThresholdSignatureInspector) enoughShares() bool {
   262  	// len(s.signers) is always <= s.threshold + 1
   263  	return len(s.shares) == (s.threshold + 1)
   264  }
   265  
   266  // HasShare checks whether the internal map contains the share of the given index.
   267  // This function is thread safe and locks the internal state.
   268  // The function returns:
   269  //   - (false, invalidInputsError) if the index is invalid
   270  //   - (false, nil) if index is valid and share is not in the map
   271  //   - (true, nil) if index is valid and share is in the map
   272  func (s *blsThresholdSignatureInspector) HasShare(orig int) (bool, error) {
   273  	// validate index
   274  	if err := s.validIndex(orig); err != nil {
   275  		return false, err
   276  	}
   277  
   278  	s.lock.RLock()
   279  	defer s.lock.RUnlock()
   280  
   281  	return s.hasShare(index(orig)), nil
   282  }
   283  
   284  // non thread safe version of HasShare, and assumes input is valid
   285  func (s *blsThresholdSignatureInspector) hasShare(orig index) bool {
   286  	_, ok := s.shares[orig]
   287  	return ok
   288  }
   289  
   290  // TrustedAdd adds a signature share to the internal pool of shares
   291  // without verifying the signature against the message and the participant's
   292  // public key. This function is thread safe and locks the internal state.
   293  //
   294  // The share is only added if the signer index is valid and has not been
   295  // added yet. Moreover, the share is added only if not enough shares were collected.
   296  // The function returns:
   297  //   - (true, nil) if enough signature shares were already collected and no error occurred
   298  //   - (false, nil) if not enough shares were collected and no error occurred
   299  //   - (false, invalidInputsError) if index is invalid
   300  //   - (false, duplicatedSignerError) if a signature for the index was previously added
   301  func (s *blsThresholdSignatureInspector) TrustedAdd(orig int, share Signature) (bool, error) {
   302  	// validate index
   303  	if err := s.validIndex(orig); err != nil {
   304  		return false, err
   305  	}
   306  
   307  	s.lock.Lock()
   308  	defer s.lock.Unlock()
   309  
   310  	if s.hasShare(index(orig)) {
   311  		return false, duplicatedSignerErrorf("share for %d was already added", orig)
   312  	}
   313  
   314  	if s.enoughShares() {
   315  		return true, nil
   316  	}
   317  	s.shares[index(orig)] = share
   318  	return s.enoughShares(), nil
   319  }
   320  
   321  // VerifyAndAdd verifies a signature share (same as `VerifyShare`),
   322  // and may or may not add the share to the local pool of shares.
   323  // This function is thread safe and locks the internal state.
   324  //
   325  // The share is only added if the signature is valid, the signer index is valid and has not been
   326  // added yet. Moreover, the share is added only if not enough shares were collected.
   327  // Boolean returns:
   328  //   - First boolean output is true if the share is valid and no error is returned, and false otherwise.
   329  //   - Second boolean output is true if enough shares were collected and no error is returned, and false otherwise.
   330  //
   331  // Error returns:
   332  //   - invalidInputsError if input index is invalid. A signature that doesn't verify against the signer's
   333  //     public key is not considered an invalid input.
   334  //   - duplicatedSignerError if signer was already added.
   335  //   - other errors if an unexpected exception occurred.
   336  func (s *blsThresholdSignatureInspector) VerifyAndAdd(orig int, share Signature) (bool, bool, error) {
   337  	// validate index
   338  	if err := s.validIndex(orig); err != nil {
   339  		return false, false, err
   340  	}
   341  
   342  	s.lock.Lock()
   343  	defer s.lock.Unlock()
   344  
   345  	// check share is new
   346  	if s.hasShare(index(orig)) {
   347  		return false, false, duplicatedSignerErrorf("share for %d was already added", orig)
   348  	}
   349  
   350  	// verify the share
   351  	verif, err := s.publicKeyShares[index(orig)].Verify(share, s.message, s.hasher)
   352  	if err != nil {
   353  		return false, false, fmt.Errorf("verification of share failed: %w", err)
   354  	}
   355  
   356  	enough := s.enoughShares()
   357  	if verif && !enough {
   358  		s.shares[index(orig)] = share
   359  	}
   360  	return verif, s.enoughShares(), nil
   361  }
   362  
   363  // ThresholdSignature returns the threshold signature if the threshold was reached.
   364  // The threshold signature is reconstructed only once is cached for subsequent calls.
   365  //
   366  // The function is thread-safe.
   367  // Returns:
   368  //   - (signature, nil) if no error occurred
   369  //   - (nil, notEnoughSharesError) if not enough shares were collected
   370  //   - (nil, invalidSignatureError) if at least one collected share does not serialize to a valid BLS signature.
   371  //   - (nil, invalidInputsError) if the constructed signature failed to verify against the group public key and stored
   372  //     message. This post-verification is required  for safety, as `TrustedAdd` allows adding invalid signatures.
   373  //   - (nil, error) for any other unexpected error.
   374  func (s *blsThresholdSignatureInspector) ThresholdSignature() (Signature, error) {
   375  	s.lock.Lock()
   376  	defer s.lock.Unlock()
   377  
   378  	// check cached thresholdSignature
   379  	if s.thresholdSignature != nil {
   380  		return s.thresholdSignature, nil
   381  	}
   382  
   383  	// reconstruct the threshold signature
   384  	thresholdSignature, err := s.reconstructThresholdSignature()
   385  	if err != nil {
   386  		return nil, err
   387  	}
   388  	s.thresholdSignature = thresholdSignature
   389  	return thresholdSignature, nil
   390  }
   391  
   392  // reconstructThresholdSignature reconstructs the threshold signature from at least (t+1) shares.
   393  // Returns:
   394  //   - (signature, nil) if no error occurred
   395  //   - (nil, notEnoughSharesError) if not enough shares were collected
   396  //   - (nil, invalidSignatureError) if at least one collected share does not serialize to a valid BLS signature.
   397  //   - (nil, invalidInputsError) if the constructed signature failed to verify against the group public key and stored message.
   398  //   - (nil, error) for any other unexpected error.
   399  func (s *blsThresholdSignatureInspector) reconstructThresholdSignature() (Signature, error) {
   400  
   401  	if !s.enoughShares() {
   402  		return nil, notEnoughSharesErrorf("number of signature shares %d is not enough, %d are required",
   403  			len(s.shares), s.threshold+1)
   404  	}
   405  	thresholdSignature := make([]byte, signatureLengthBLSBLS12381)
   406  
   407  	// prepare the C layer inputs
   408  	shares := make([]byte, 0, len(s.shares)*signatureLengthBLSBLS12381)
   409  	signers := make([]index, 0, len(s.shares))
   410  	for index, share := range s.shares {
   411  		shares = append(shares, share...)
   412  		signers = append(signers, index)
   413  	}
   414  
   415  	// set BLS settings
   416  	blsInstance.reInit()
   417  
   418  	// Lagrange Interpolate at point 0
   419  	result := C.G1_lagrangeInterpolateAtZero(
   420  		(*C.uchar)(&thresholdSignature[0]),
   421  		(*C.uchar)(&shares[0]),
   422  		(*C.uint8_t)(&signers[0]), (C.int)(s.threshold+1))
   423  
   424  	if result != valid {
   425  		return nil, invalidSignatureError
   426  	}
   427  
   428  	// Verify the computed signature
   429  	verif, err := s.VerifyThresholdSignature(thresholdSignature)
   430  	if err != nil {
   431  		return nil, fmt.Errorf("internal error while verifying the threshold signature: %w", err)
   432  	}
   433  	if !verif {
   434  		return nil, invalidInputsErrorf(
   435  			"constructed threshold signature does not verify against the group public key, check shares and public key")
   436  	}
   437  
   438  	return thresholdSignature, nil
   439  }
   440  
   441  // BLSReconstructThresholdSignature is a stateless BLS api that takes a list of
   442  // BLS signatures and their signers' indices and returns the threshold signature.
   443  //
   444  // size is the number of participants, it must be in the range [ThresholdSignMinSize..ThresholdSignMaxSize].
   445  // threshold is the threshold value, it must be in the range [MinimumThreshold..size-1].
   446  // The function does not check the validity of the shares, and does not check
   447  // the validity of the resulting signature.
   448  // BLSReconstructThresholdSignature returns:
   449  //   - (nil, error) if the inputs are not in the correct range, if the threshold is not reached
   450  //   - (nil, duplicatedSignerError) if input signers are not distinct.
   451  //   - (nil, invalidSignatureError) if at least one of the first (threshold+1) signatures.
   452  //     does not serialize to a valid E1 point.
   453  //   - (threshold_sig, nil) otherwise.
   454  //
   455  // If the number of shares reaches the required threshold, only the first threshold+1 shares
   456  // are considered to reconstruct the signature.
   457  func BLSReconstructThresholdSignature(size int, threshold int,
   458  	shares []Signature, signers []int) (Signature, error) {
   459  	// set BLS settings
   460  	blsInstance.reInit()
   461  
   462  	if size < ThresholdSignMinSize || size > ThresholdSignMaxSize {
   463  		return nil, invalidInputsErrorf(
   464  			"size should be between %d and %d",
   465  			ThresholdSignMinSize,
   466  			ThresholdSignMaxSize)
   467  	}
   468  	if threshold >= size || threshold < MinimumThreshold {
   469  		return nil, invalidInputsErrorf(
   470  			"the threshold must be between %d and %d, got %d",
   471  			MinimumThreshold, size-1,
   472  			threshold)
   473  	}
   474  
   475  	if len(shares) != len(signers) {
   476  		return nil, invalidInputsErrorf(
   477  			"the number of signature shares is not matching the number of signers")
   478  	}
   479  
   480  	if len(shares) < threshold+1 {
   481  		return nil, invalidInputsErrorf(
   482  			"the number of signatures does not reach the threshold")
   483  	}
   484  
   485  	// map to check signers are distinct
   486  	m := make(map[index]bool)
   487  
   488  	// flatten the shares (required by the C layer)
   489  	flatShares := make([]byte, 0, signatureLengthBLSBLS12381*(threshold+1))
   490  	indexSigners := make([]index, 0, threshold+1)
   491  	for i, share := range shares {
   492  		flatShares = append(flatShares, share...)
   493  		// check the index is valid
   494  		if signers[i] >= size || signers[i] < 0 {
   495  			return nil, invalidInputsErrorf(
   496  				"signer index #%d is invalid", i)
   497  		}
   498  		// check the index is new
   499  		if _, isSeen := m[index(signers[i])]; isSeen {
   500  			return nil, duplicatedSignerErrorf(
   501  				"%d is a duplicate signer", index(signers[i]))
   502  		}
   503  		m[index(signers[i])] = true
   504  		indexSigners = append(indexSigners, index(signers[i]))
   505  	}
   506  
   507  	thresholdSignature := make([]byte, signatureLengthBLSBLS12381)
   508  	// Lagrange Interpolate at point 0
   509  	if C.G1_lagrangeInterpolateAtZero(
   510  		(*C.uchar)(&thresholdSignature[0]),
   511  		(*C.uchar)(&flatShares[0]),
   512  		(*C.uint8_t)(&indexSigners[0]), (C.int)(threshold+1),
   513  	) != valid {
   514  		return nil, invalidSignatureError
   515  	}
   516  	return thresholdSignature, nil
   517  }
   518  
   519  // EnoughShares is a stateless function that takes the value of the threshold
   520  // and a shares number and returns true if the shares number is enough
   521  // to reconstruct a threshold signature.
   522  //
   523  // The function returns:
   524  //   - (false, invalidInputsErrorf) if input threshold is less than 1
   525  //   - (false, nil) if threshold is valid but shares are not enough.
   526  //   - (true, nil) if the threshold is valid but shares are enough.
   527  func EnoughShares(threshold int, sharesNumber int) (bool, error) {
   528  	if threshold < MinimumThreshold {
   529  		return false, invalidInputsErrorf(
   530  			"the threshold can't be smaller than %d, got %d",
   531  			MinimumThreshold, threshold)
   532  	}
   533  	return sharesNumber > threshold, nil
   534  }
   535  
   536  // BLSThresholdKeyGen is a key generation for a BLS-based
   537  // threshold signature scheme with a trusted dealer.
   538  //
   539  // The function returns :
   540  //   - (nil, nil, nil, invalidInputsErrorf) if:
   541  //   - n is not in [`ThresholdSignMinSize`, `ThresholdSignMaxSize`]
   542  //   - threshold value is not in interval [1, n-1]
   543  //   - (groupPrivKey, []pubKeyShares, groupPubKey, nil) otherwise
   544  func BLSThresholdKeyGen(size int, threshold int, seed []byte) ([]PrivateKey,
   545  	[]PublicKey, PublicKey, error) {
   546  	if size < ThresholdSignMinSize || size > ThresholdSignMaxSize {
   547  		return nil, nil, nil, invalidInputsErrorf(
   548  			"size should be between %d and %d, got %d",
   549  			ThresholdSignMinSize,
   550  			ThresholdSignMaxSize,
   551  			size)
   552  	}
   553  	if threshold >= size || threshold < MinimumThreshold {
   554  		return nil, nil, nil, invalidInputsErrorf(
   555  			"the threshold must be between %d and %d, got %d",
   556  			MinimumThreshold,
   557  			size-1,
   558  			threshold)
   559  	}
   560  
   561  	// set BLS settings
   562  	blsInstance.reInit()
   563  
   564  	// the scalars x and G2 points y
   565  	x := make([]scalar, size)
   566  	y := make([]pointG2, size)
   567  	var X0 pointG2
   568  
   569  	// seed relic
   570  	if err := seedRelic(seed); err != nil {
   571  		return nil, nil, nil, fmt.Errorf("seeding relic failed: %w", err)
   572  	}
   573  	// Generate a polynomial P in Zr[X] of degree t
   574  	a := make([]scalar, threshold+1)
   575  	randZrStar(&a[0]) // non-identity key
   576  	if threshold > 0 {
   577  		for i := 1; i < threshold; i++ {
   578  			randZr(&a[i])
   579  		}
   580  		randZrStar(&a[threshold]) // enforce the polynomial degree
   581  	}
   582  	// compute the shares
   583  	for i := index(1); int(i) <= size; i++ {
   584  		C.Zr_polynomialImage(
   585  			(*C.bn_st)(&x[i-1]),
   586  			(*C.ep2_st)(&y[i-1]),
   587  			(*C.bn_st)(&a[0]), (C.int)(len(a)),
   588  			(C.uint8_t)(i),
   589  		)
   590  	}
   591  	// group public key
   592  	generatorScalarMultG2(&X0, &a[0])
   593  	// export the keys
   594  	skShares := make([]PrivateKey, size)
   595  	pkShares := make([]PublicKey, size)
   596  	var pkGroup PublicKey
   597  	for i := 0; i < size; i++ {
   598  		skShares[i] = newPrKeyBLSBLS12381(&x[i])
   599  		pkShares[i] = newPubKeyBLSBLS12381(&y[i])
   600  	}
   601  	pkGroup = newPubKeyBLSBLS12381(&X0)
   602  
   603  	// public key shares and group public key
   604  	// are sampled uniformly at random. The probability of
   605  	// generating an identity key is therefore negligible.
   606  	return skShares, pkShares, pkGroup, nil
   607  }