github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/verification/common.go (about)

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