github.com/prysmaticlabs/prysm@v1.4.4/shared/bls/blst/signature.go (about)

     1  // +build linux,amd64 linux,arm64 darwin,amd64 windows,amd64
     2  // +build !blst_disabled
     3  
     4  package blst
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"sync"
    10  
    11  	"github.com/pkg/errors"
    12  	"github.com/prysmaticlabs/prysm/shared/bls/common"
    13  	"github.com/prysmaticlabs/prysm/shared/featureconfig"
    14  	"github.com/prysmaticlabs/prysm/shared/params"
    15  	"github.com/prysmaticlabs/prysm/shared/rand"
    16  	blst "github.com/supranational/blst/bindings/go"
    17  )
    18  
    19  var dst = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_")
    20  
    21  const scalarBytes = 32
    22  const randBitsEntropy = 64
    23  
    24  // Signature used in the BLS signature scheme.
    25  type Signature struct {
    26  	s *blstSignature
    27  }
    28  
    29  // SignatureFromBytes creates a BLS signature from a LittleEndian byte slice.
    30  func SignatureFromBytes(sig []byte) (common.Signature, error) {
    31  	if featureconfig.Get().SkipBLSVerify {
    32  		return &Signature{}, nil
    33  	}
    34  	if len(sig) != params.BeaconConfig().BLSSignatureLength {
    35  		return nil, fmt.Errorf("signature must be %d bytes", params.BeaconConfig().BLSSignatureLength)
    36  	}
    37  	signature := new(blstSignature).Uncompress(sig)
    38  	if signature == nil {
    39  		return nil, errors.New("could not unmarshal bytes into signature")
    40  	}
    41  	// Group check signature. Do not check for infinity since an aggregated signature
    42  	// could be infinite.
    43  	if !signature.SigValidate(false) {
    44  		return nil, errors.New("signature not in group")
    45  	}
    46  	return &Signature{s: signature}, nil
    47  }
    48  
    49  // Verify a bls signature given a public key, a message.
    50  //
    51  // In IETF draft BLS specification:
    52  // Verify(PK, message, signature) -> VALID or INVALID: a verification
    53  //      algorithm that outputs VALID if signature is a valid signature of
    54  //      message under public key PK, and INVALID otherwise.
    55  //
    56  // In the Ethereum proof of stake specification:
    57  // def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool
    58  func (s *Signature) Verify(pubKey common.PublicKey, msg []byte) bool {
    59  	if featureconfig.Get().SkipBLSVerify {
    60  		return true
    61  	}
    62  	// Signature and PKs are assumed to have been validated upon decompression!
    63  	return s.s.Verify(false, pubKey.(*PublicKey).p, false, msg, dst)
    64  }
    65  
    66  // AggregateVerify verifies each public key against its respective message. This is vulnerable to
    67  // rogue public-key attack. Each user must provide a proof-of-knowledge of the public key.
    68  //
    69  // Note: The msgs must be distinct. For maximum performance, this method does not ensure distinct
    70  // messages.
    71  //
    72  // In IETF draft BLS specification:
    73  // AggregateVerify((PK_1, message_1), ..., (PK_n, message_n),
    74  //      signature) -> VALID or INVALID: an aggregate verification
    75  //      algorithm that outputs VALID if signature is a valid aggregated
    76  //      signature for a collection of public keys and messages, and
    77  //      outputs INVALID otherwise.
    78  //
    79  // In the Ethereum proof of stake specification:
    80  // def AggregateVerify(pairs: Sequence[PK: BLSPubkey, message: Bytes], signature: BLSSignature) -> bool
    81  //
    82  // Deprecated: Use FastAggregateVerify or use this method in spectests only.
    83  func (s *Signature) AggregateVerify(pubKeys []common.PublicKey, msgs [][32]byte) bool {
    84  	if featureconfig.Get().SkipBLSVerify {
    85  		return true
    86  	}
    87  	size := len(pubKeys)
    88  	if size == 0 {
    89  		return false
    90  	}
    91  	if size != len(msgs) {
    92  		return false
    93  	}
    94  	msgSlices := make([][]byte, len(msgs))
    95  	rawKeys := make([]*blstPublicKey, len(msgs))
    96  	for i := 0; i < size; i++ {
    97  		msgSlices[i] = msgs[i][:]
    98  		rawKeys[i] = pubKeys[i].(*PublicKey).p
    99  	}
   100  	// Signature and PKs are assumed to have been validated upon decompression!
   101  	return s.s.AggregateVerify(false, rawKeys, false, msgSlices, dst)
   102  }
   103  
   104  // FastAggregateVerify verifies all the provided public keys with their aggregated signature.
   105  //
   106  // In IETF draft BLS specification:
   107  // FastAggregateVerify(PK_1, ..., PK_n, message, signature) -> VALID
   108  //      or INVALID: a verification algorithm for the aggregate of multiple
   109  //      signatures on the same message.  This function is faster than
   110  //      AggregateVerify.
   111  //
   112  // In the Ethereum proof of stake specification:
   113  // def FastAggregateVerify(PKs: Sequence[BLSPubkey], message: Bytes, signature: BLSSignature) -> bool
   114  func (s *Signature) FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool {
   115  	if featureconfig.Get().SkipBLSVerify {
   116  		return true
   117  	}
   118  	if len(pubKeys) == 0 {
   119  		return false
   120  	}
   121  	rawKeys := make([]*blstPublicKey, len(pubKeys))
   122  	for i := 0; i < len(pubKeys); i++ {
   123  		rawKeys[i] = pubKeys[i].(*PublicKey).p
   124  	}
   125  
   126  	return s.s.FastAggregateVerify(true, rawKeys, msg[:], dst)
   127  }
   128  
   129  // Eth2FastAggregateVerify implements a wrapper on top of bls's FastAggregateVerify. It accepts G2_POINT_AT_INFINITY signature
   130  // when pubkeys empty.
   131  //
   132  // Spec code:
   133  // def eth2_fast_aggregate_verify(pubkeys: Sequence[BLSPubkey], message: Bytes32, signature: BLSSignature) -> bool:
   134  //    """
   135  //    Wrapper to ``bls.FastAggregateVerify`` accepting the ``G2_POINT_AT_INFINITY`` signature when ``pubkeys`` is empty.
   136  //    """
   137  //    if len(pubkeys) == 0 and signature == G2_POINT_AT_INFINITY:
   138  //        return True
   139  //    return bls.FastAggregateVerify(pubkeys, message, signature)
   140  func (s *Signature) Eth2FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool {
   141  	if featureconfig.Get().SkipBLSVerify {
   142  		return true
   143  	}
   144  	g2PointAtInfinity := append([]byte{0xC0}, make([]byte, 95)...)
   145  	if len(pubKeys) == 0 && bytes.Equal(s.Marshal(), g2PointAtInfinity) {
   146  		return true
   147  	}
   148  	rawKeys := make([]*blstPublicKey, len(pubKeys))
   149  	for i := 0; i < len(pubKeys); i++ {
   150  		rawKeys[i] = pubKeys[i].(*PublicKey).p
   151  	}
   152  
   153  	return s.s.FastAggregateVerify(true, rawKeys, msg[:], dst)
   154  }
   155  
   156  // NewAggregateSignature creates a blank aggregate signature.
   157  func NewAggregateSignature() common.Signature {
   158  	sig := blst.HashToG2([]byte{'m', 'o', 'c', 'k'}, dst).ToAffine()
   159  	return &Signature{s: sig}
   160  }
   161  
   162  // AggregateSignatures converts a list of signatures into a single, aggregated sig.
   163  func AggregateSignatures(sigs []common.Signature) common.Signature {
   164  	if len(sigs) == 0 {
   165  		return nil
   166  	}
   167  	if featureconfig.Get().SkipBLSVerify {
   168  		return sigs[0]
   169  	}
   170  
   171  	rawSigs := make([]*blstSignature, len(sigs))
   172  	for i := 0; i < len(sigs); i++ {
   173  		rawSigs[i] = sigs[i].(*Signature).s
   174  	}
   175  
   176  	// Signature and PKs are assumed to have been validated upon decompression!
   177  	signature := new(blstAggregateSignature)
   178  	signature.Aggregate(rawSigs, false)
   179  	return &Signature{s: signature.ToAffine()}
   180  }
   181  
   182  // Aggregate is an alias for AggregateSignatures, defined to conform to BLS specification.
   183  //
   184  // In IETF draft BLS specification:
   185  // Aggregate(signature_1, ..., signature_n) -> signature: an
   186  //      aggregation algorithm that compresses a collection of signatures
   187  //      into a single signature.
   188  //
   189  // In the Ethereum proof of stake specification:
   190  // def Aggregate(signatures: Sequence[BLSSignature]) -> BLSSignature
   191  //
   192  // Deprecated: Use AggregateSignatures.
   193  func Aggregate(sigs []common.Signature) common.Signature {
   194  	return AggregateSignatures(sigs)
   195  }
   196  
   197  // VerifyMultipleSignatures verifies a non-singular set of signatures and its respective pubkeys and messages.
   198  // This method provides a safe way to verify multiple signatures at once. We pick a number randomly from 1 to max
   199  // uint64 and then multiply the signature by it. We continue doing this for all signatures and its respective pubkeys.
   200  // S* = S_1 * r_1 + S_2 * r_2 + ... + S_n * r_n
   201  // P'_{i,j} = P_{i,j} * r_i
   202  // e(S*, G) = \prod_{i=1}^n \prod_{j=1}^{m_i} e(P'_{i,j}, M_{i,j})
   203  // Using this we can verify multiple signatures safely.
   204  func VerifyMultipleSignatures(sigs [][]byte, msgs [][32]byte, pubKeys []common.PublicKey) (bool, error) {
   205  	if featureconfig.Get().SkipBLSVerify {
   206  		return true, nil
   207  	}
   208  	if len(sigs) == 0 || len(pubKeys) == 0 {
   209  		return false, nil
   210  	}
   211  	rawSigs := new(blstSignature).BatchUncompress(sigs)
   212  
   213  	length := len(sigs)
   214  	if length != len(pubKeys) || length != len(msgs) {
   215  		return false, errors.Errorf("provided signatures, pubkeys and messages have differing lengths. S: %d, P: %d,M %d",
   216  			length, len(pubKeys), len(msgs))
   217  	}
   218  	mulP1Aff := make([]*blstPublicKey, length)
   219  	rawMsgs := make([]blst.Message, length)
   220  
   221  	for i := 0; i < length; i++ {
   222  		mulP1Aff[i] = pubKeys[i].(*PublicKey).p
   223  		rawMsgs[i] = msgs[i][:]
   224  	}
   225  	// Secure source of RNG
   226  	randGen := rand.NewGenerator()
   227  	randLock := new(sync.Mutex)
   228  
   229  	randFunc := func(scalar *blst.Scalar) {
   230  		var rbytes [scalarBytes]byte
   231  		randLock.Lock()
   232  		// Ignore error as the error will always be nil in `read` in math/rand.
   233  		randGen.Read(rbytes[:])
   234  		randLock.Unlock()
   235  		// Protect against the generator returning 0. Since the scalar value is
   236  		// derived from a big endian byte slice, we take the last byte.
   237  		rbytes[len(rbytes)-1] |= 0x01
   238  		scalar.FromBEndian(rbytes[:])
   239  	}
   240  	dummySig := new(blstSignature)
   241  
   242  	// Validate signatures since we uncompress them here. Public keys should already be validated.
   243  	return dummySig.MultipleAggregateVerify(rawSigs, true, mulP1Aff, false, rawMsgs, dst, randFunc, randBitsEntropy), nil
   244  }
   245  
   246  // Marshal a signature into a LittleEndian byte slice.
   247  func (s *Signature) Marshal() []byte {
   248  	if featureconfig.Get().SkipBLSVerify {
   249  		return make([]byte, params.BeaconConfig().BLSSignatureLength)
   250  	}
   251  
   252  	return s.s.Compress()
   253  }
   254  
   255  // Copy returns a full deep copy of a signature.
   256  func (s *Signature) Copy() common.Signature {
   257  	sign := *s.s
   258  	return &Signature{s: &sign}
   259  }
   260  
   261  // VerifyCompressed verifies that the compressed signature and pubkey
   262  // are valid from the message provided.
   263  func VerifyCompressed(signature, pub, msg []byte) bool {
   264  	// Validate signature and PKs since we will uncompress them here
   265  	return new(blstSignature).VerifyCompressed(signature, true, pub, true, msg, dst)
   266  }