-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Identify invalid signature within batch verification (#11582) #11741
Identify invalid signature within batch verification (#11582) #11741
Conversation
@terencechain could you have some time to review this PR? I'm not sure if |
@@ -216,11 +218,14 @@ func createAttestationSignatureBatch( | |||
return nil, errors.Wrap(err, "could not get signing root of object") | |||
} | |||
msgs[i] = root | |||
|
|||
descs[i] = "attestation signature" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of hard-coding these descriptions, maybe we can use enums
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks better, thanks!
I dont think |
crypto/bls/signature_batch.go
Outdated
@@ -25,6 +32,11 @@ func (s *SignatureBatch) Join(set *SignatureBatch) *SignatureBatch { | |||
s.Signatures = append(s.Signatures, set.Signatures...) | |||
s.PublicKeys = append(s.PublicKeys, set.PublicKeys...) | |||
s.Messages = append(s.Messages, set.Messages...) | |||
if s.Descriptions != nil && set.Descriptions != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this nil check, since all signature sets now have a description
attached to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because SignatureBatch
is such a fundamental struct so I hesitated to make a big change, then ended up a compromised solution that SignatureBatch
can have either full description list or nil
. But I think your comment is absolutely correct, it should be far more elegant if every signature has a description
attached. I'll make the change as you commented. Thanks for your advice!
crypto/bls/signature_batch.go
Outdated
if err != nil { | ||
_, e := fmt.Fprintf(&sb, "signature '%s' is invalid."+ | ||
" signature: %v, public key: %v, message: %v, error: %v\n", desc, sig, pubKey, msg, err) | ||
if e != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't ignore errors here, also why are we using Fprintf
here ? Wrap the error and then return it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I used strings.Builder
for better performance (not a big problem though). strings.Builder
follows StringWriter
interface that emits error but actually always returns nil
as error, so I decided to ignore it. I'll consider to use string concatenation instead.
crypto/bls/signature_batch.go
Outdated
valid, err := VerifySignature(sig, msg, pubKey) | ||
if !valid { | ||
var desc string | ||
if len(s.Descriptions) != len(s.Signatures) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These checks are going to be ugly and a source of bugs, it would be much better to always have the same number of descriptions and signatures in a set.
}, nil | ||
} | ||
|
||
// verifies the signature from the raw data, public key and domain provided. | ||
func verifySignature(signedData, pub, signature, domain []byte) error { | ||
set, err := signatureBatch(signedData, pub, signature, domain) | ||
set, err := signatureBatch(signedData, pub, signature, domain, "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better to name it as "unknown" rather than an empty string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As @terencechain's comment, I'll use enums instead. Thanks!
crypto/bls/signature_batch.go
Outdated
} | ||
} else { | ||
_, e := fmt.Fprintf(&sb, "signature '%s' is invalid."+ | ||
" signature: %v, public key: %v, message: %v\n", desc, sig, pubKey, msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to represent msg
as hex rather than an array of bytes when printing the error message. Same applies to signature and public keys. Also you need to marshal the public key into bytes otherwise you end up printing pointers to the struct which isn't useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I'll make error message more readable.
crypto/bls/signature_batch.go
Outdated
@@ -82,6 +145,10 @@ func (s *SignatureBatch) RemoveDuplicates() (int, *SignatureBatch, error) { | |||
sigs := s.Signatures[:0] | |||
pubs := s.PublicKeys[:0] | |||
msgs := s.Messages[:0] | |||
var descs []string | |||
if s.Descriptions != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same thing here, do not have a nil check here. The correct thing to do would be to make sure all signature sets always have an attached description.
I have done the changes, please take a review when you have time. Thanks! @nisdas @terencechain |
1295f6e
to
fb62cdd
Compare
crypto/bls/constants.go
Outdated
@@ -5,3 +5,31 @@ const DomainByteLength = 4 | |||
|
|||
// CurveOrder for the BLS12-381 curve. | |||
const CurveOrder = "52435875175126190479447740508185965837690552500527637822603658699938581184513" | |||
|
|||
// List of descriptions for different kinds of signatures | |||
const ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the wrong place to declare these descriptions. The bls library should have no knowledge of what is an application construct( bls change, randao, etc)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your comment! I have moved these constants to signing package, but left AggregatedSignature
in bls package as it is a local knowledge belongs to bls package.
@@ -140,6 +195,7 @@ func (s *SignatureBatch) AggregateBatch() (*SignatureBatch, error) { | |||
b.PublicKeys = []PublicKey{aggPub} | |||
b.Signatures = [][]byte{aggSig.Marshal()} | |||
b.Messages = [][32]byte{copiedRt} | |||
b.Descriptions = []string{AggregatedSignature} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one worry is that you get information loss with this, since you lose the ability to determine which type of signature was invalid. Its instead just a generic AggregatedSignature
Usually messages with the same root, will reference the same types of messages(attestations,sync committee,etc) . But we can maybe fix this in a follow up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about the information loss, but still used a generic description here for two reason:
- the signatures themself are aggregated and we can hardly identify invalid individual signature.
AggregateBatch()
is called invalidateWithBatchVerifier()
but notVerifyVerbosely()
, currently onlyVerifyVerbosely()
is responsible to nail down failed signature.
But I'm not sure if I have fully understood the whole picture of block processing (hence original issue #11582), correct me if I am wrong! Thanks! Maybe as you said, we can fix this later.
@@ -119,12 +172,14 @@ func (s *SignatureBatch) AggregateBatch() (*SignatureBatch, error) { | |||
currBatch.Signatures = append(currBatch.Signatures, s.Signatures[i]) | |||
currBatch.Messages = append(currBatch.Messages, s.Messages[i]) | |||
currBatch.PublicKeys = append(currBatch.PublicKeys, s.PublicKeys[i]) | |||
currBatch.Descriptions = append(currBatch.Descriptions, s.Descriptions[i]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should also do a length check for number of descriptions, otherwise this will panic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added length validation for descriptions. Thanks for the comment!
I reordered the empty check and length check because I think length mismatch of signatures and messages should always be an error even in case that signatures are empty and messages are non-empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing all the comments, great work on this feature !
Build is failing @dyng
|
@nisdas I have fixed build failure, sorry for that (I should build locally at first). Thanks for your review and precious comments! |
What type of PR is this?
Feature
What does this PR do? Why is it needed?
The signatures of newly received blocks are collected and verified as a batch, while it's perfect if everything is ok, we can't tell which signature is invalid if batch verification fails.
Which issues(s) does this PR fix?
Fixes #11582
Other notes for review
This PR is not yet complete because I need to discuss with reviewer to determine where
VerifyVerbosely
is supposed to be used.