github.com/onflow/flow-go@v0.33.17/consensus/hotstuff/verification/common.go (about)

     1  package verification
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/consensus/hotstuff/model"
     7  	"github.com/onflow/flow-go/crypto"
     8  	"github.com/onflow/flow-go/crypto/hash"
     9  	"github.com/onflow/flow-go/model/flow"
    10  
    11  	"encoding/binary"
    12  )
    13  
    14  // MakeVoteMessage generates the message we have to sign in order to be able
    15  // to verify signatures without having the full block. To that effect, each data
    16  // structure that is signed contains the sometimes redundant view number and
    17  // block ID; this allows us to create the signed message and verify the signed
    18  // message without having the full block contents.
    19  func MakeVoteMessage(view uint64, blockID flow.Identifier) []byte {
    20  	msg := make([]byte, 8, 8+flow.IdentifierLen)
    21  	binary.BigEndian.PutUint64(msg, view)
    22  	msg = append(msg, blockID[:]...)
    23  	return msg
    24  }
    25  
    26  // MakeTimeoutMessage generates the message we have to sign in order to be able
    27  // to contribute to Active Pacemaker protocol. Each replica signs with the highest QC view
    28  // known to that replica.
    29  func MakeTimeoutMessage(view uint64, newestQCView uint64) []byte {
    30  	msg := make([]byte, 16)
    31  	binary.BigEndian.PutUint64(msg[:8], view)
    32  	binary.BigEndian.PutUint64(msg[8:], newestQCView)
    33  	return msg
    34  }
    35  
    36  // verifyAggregatedSignatureOneMessage encapsulates the logic of verifying an aggregated signature
    37  // under the same message.
    38  // Proofs of possession of all input keys are assumed to be valid (checked by the protocol).
    39  // This logic is commonly used across the different implementations of `hotstuff.Verifier`.
    40  // In this context, all signatures apply to blocks.
    41  // Return values:
    42  //   - nil if `aggregatedSig` is valid against the public keys and message.
    43  //   - model.InsufficientSignaturesError if `pubKeys` is empty or nil.
    44  //   - model.ErrInvalidSignature if the signature is invalid against the public keys and message.
    45  //   - unexpected errors should be treated as symptoms of bugs or uncovered
    46  //     edge cases in the logic (i.e. as fatal)
    47  func verifyAggregatedSignatureOneMessage(
    48  	pubKeys []crypto.PublicKey, // public keys of actors to verify against
    49  	aggregatedSig crypto.Signature, // aggregated signature to be checked
    50  	hasher hash.Hasher, // hasher (contains usage-specific domain-separation tag)
    51  	msg []byte, // message to verify against
    52  ) error {
    53  	// TODO: as further optimization, replace the following call with model/signature.PublicKeyAggregator
    54  	// the function could accept the public key aggrgator as an input
    55  	aggregatedKey, err := crypto.AggregateBLSPublicKeys(pubKeys)
    56  	if err != nil {
    57  		// `AggregateBLSPublicKeys` returns an error in two distinct cases:
    58  		//  (i) In case no keys are provided, i.e. `len(pubKeys) == 0`.
    59  		//      This scenario _is expected_ during normal operations, because a byzantine
    60  		//      proposer might construct an (invalid) QC with an empty list of signers.
    61  		// (ii) In case some provided public keys type is not BLS.
    62  		//      This scenario is _not expected_ during normal operations, because all keys are
    63  		//      guaranteed by the protocol to be BLS keys.
    64  		if crypto.IsBLSAggregateEmptyListError(err) { // check case (i)
    65  			return model.NewInsufficientSignaturesErrorf("aggregating public keys failed: %w", err)
    66  		}
    67  		// case (ii) or any other error are not expected during normal operations
    68  		return fmt.Errorf("internal error computing aggregated key: %w", err)
    69  	}
    70  
    71  	valid, err := aggregatedKey.Verify(aggregatedSig, msg, hasher)
    72  	if err != nil {
    73  		return fmt.Errorf("internal error while verifying aggregated signature: %w", err)
    74  	}
    75  	if !valid {
    76  		return fmt.Errorf("invalid aggregated signature: %w", model.ErrInvalidSignature)
    77  	}
    78  	return nil
    79  }
    80  
    81  // verifyTCSignatureManyMessages checks cryptographic validity of the TC's signature w.r.t.
    82  // multiple messages and public keys.
    83  // Proofs of possession of all input keys are assumed to be valid (checked by the protocol).
    84  // This logic is commonly used across the different implementations of `hotstuff.Verifier`.
    85  // It is the responsibility of the calling code to ensure that all `pks` are authorized,
    86  // without duplicates. The caller must also make sure the `hasher` passed is non nil and has
    87  // 128-bytes outputs.
    88  // Return values:
    89  //   - nil if `sigData` is cryptographically valid
    90  //   - model.InsufficientSignaturesError if `pks` is empty.
    91  //   - model.InvalidFormatError if `pks`/`highQCViews` have differing lengths
    92  //   - model.ErrInvalidSignature if a signature is invalid
    93  //   - unexpected errors should be treated as symptoms of bugs or uncovered
    94  //     edge cases in the logic (i.e. as fatal)
    95  func verifyTCSignatureManyMessages(
    96  	pks []crypto.PublicKey,
    97  	sigData crypto.Signature,
    98  	view uint64,
    99  	highQCViews []uint64,
   100  	hasher hash.Hasher,
   101  ) error {
   102  	if len(pks) != len(highQCViews) {
   103  		return model.NewInvalidFormatErrorf("public keys and highQCViews mismatch")
   104  	}
   105  
   106  	messages := make([][]byte, 0, len(pks))
   107  	hashers := make([]hash.Hasher, 0, len(pks))
   108  	for i := 0; i < len(pks); i++ {
   109  		messages = append(messages, MakeTimeoutMessage(view, highQCViews[i]))
   110  		hashers = append(hashers, hasher)
   111  	}
   112  
   113  	valid, err := crypto.VerifyBLSSignatureManyMessages(pks, sigData, messages, hashers)
   114  	if err != nil {
   115  		// `VerifyBLSSignatureManyMessages` returns an error in a few cases:
   116  		//  (i)  In case no keys are provided, i.e. `len(pks) == 0`.
   117  		//       This scenario _is expected_ during normal operations, because a byzantine
   118  		//       proposer might construct an (invalid) TC with an empty list of signers.
   119  		// (ii)  In case some provided public keys type is not BLS.
   120  		//       This scenario is _not expected_ during normal operations, because all keys are
   121  		//       guaranteed by the protocol to be BLS keys.
   122  		// (iii) In case at least one hasher is nil. This is not expected because
   123  		//       as per the function assumption.
   124  		// (iv)  In case at least one hasher has an incorrect size. This is not expected
   125  		//       because as per the function assumption.
   126  		if crypto.IsBLSAggregateEmptyListError(err) { // check case (i)
   127  			return model.NewInsufficientSignaturesErrorf("aggregating public keys failed: %w", err)
   128  		}
   129  		// any other error is not expected during normal operations
   130  		return fmt.Errorf("signature verification failed: %w", err)
   131  	}
   132  	if !valid {
   133  		return fmt.Errorf("invalid aggregated TC signature for view %d: %w", view, model.ErrInvalidSignature)
   134  	}
   135  	return nil
   136  }