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 }