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 }