-
Notifications
You must be signed in to change notification settings - Fork 7
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
Define signature aggregation API and implement it using BLS #42
Conversation
34c157c
to
aab4125
Compare
At this stage, the legacy BLS implementation vulnerable to rouge key attack is used. The plan is to fix that before merging. There is a TODO in the code here: Also, a relevant Slack thread: https://filecoinproject.slack.com/archives/C0556MSR945/p1705514311818349 |
Suggestion for my side: separate the signing backend and the pub key resolution. If that proves not enough performance-wise, we can implement an alternative scheme, but it will be an increase in scope as it will require "extracting" the key and key format out of lotus. |
6026a99
to
68eceaa
Compare
Ok that makes sense. In that case let's only keep the creation and verification of the aggregate signatures in f3. It would be useful to know exactly what kind of signatures are coming out of the Lotus API. I'll adapt the code in this PR accordingly. But I'd still find it more intuitive if the |
IMO this should be 2 PRs, one for the refactoring of the interfaces and one for the BLS implementation. If the one for the implementation bases on the refactoring, then I think both should be merged after #37 is merged. Otherwise BLS can be merged first and the refactoring come after that and #37 's merge. |
The code used by lotus: https://github.com/filecoin-project/bls-signatures/blob/master/src/signature.rs |
68eceaa
to
dfebe88
Compare
Signed-off-by: Matej Pavlovic <[email protected]>
Signed-off-by: Matej Pavlovic <[email protected]>
Signed-off-by: Matej Pavlovic <[email protected]>
0015943
to
3a52ca1
Compare
Signed-off-by: Matej Pavlovic <[email protected]>
3a52ca1
to
f51390e
Compare
Ok, here is a new implementation of BLS signature aggregation (the rouge-key-attack-resistant version), including some unit tests. I separated the signer and the aggregator, so the signer can be easily externalized. There is one potential caveat though: The underlying
|
In filecoin the signature is over G2 |
@matejpavlovic they are merged. |
f3/api.go
Outdated
// Sign signs a message and returns the signature. | ||
Sign(msg []byte) ([]byte, error) | ||
|
||
// Verify verifies a signature sig of message msg produced by participant with ActorID. |
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.
blssignatures/aggregatesig.go
Outdated
type AggSignature struct { | ||
blsScheme *BLSScheme | ||
Sig []byte | ||
SignerIndexes []int |
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 have been using bitfield.Bitfield
here (at least when serializing) to benefit from run-length encoding.
blsScheme *BLSAggScheme | ||
sigs []indexedSig | ||
signerMask *sign.Mask |
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.
Unclear here what sigs and signerMask really are, comment.
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.
Does sign.Mask use run-length encoding or how is the list of signers stored/marshaled?
type indexedSig struct { | ||
index int | ||
sig []byte | ||
} |
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.
Comment usage here or in SigAggregator at least.
type indexedSig struct { | ||
index int | ||
sig []byte | ||
} | ||
|
||
type aggSigData struct { | ||
Sig []byte | ||
Mask []byte | ||
} |
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 think aggSigData
can just be removed and only the Sig []byte
be used. granite.go
already keeps track of the signatures being aggregated in the signers bitfield.Bitfield
of QuorumSignature
. Both are redundant, and I do not see a strong reason to pair together in the serialization of the signature the list of signers. if we need a sign.Mask
to make calls to the bdn
package, we can just locally create it. Specially if sign.Mask
does not serialize as efficiently as bitfield.Bitfield does with RLE.
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.
Also, keep in mind that the list of signers should be easily accessible from Granite's quorumState
to check for the existence of a strong quorum in the aggregated signature, and this need not be necessarily performed before the deserialization/verification of the aggregated signature.
So I would rather a verification interface that explicitly takes the list of signers than one that keeps them serialized within the aggregated signature. The bitfield of signers comes sorted by voting power anyways so it should be ok for verification/aggregation AFAIK.
return nil | ||
} | ||
|
||
aggPubKey, err := bdn.AggregatePublicKeys(s.suite, mask) |
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 uses G2? Do we use G2 for public keys?
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.
bdn right now supports only G2 pubkeys, we will have to write support for G1 in kyber
// as this AggSignatureScheme, otherwise the behavior of VerifyAggSig is undefined. | ||
// VerifyAggSig returns nil if sig is a valid aggregate signature of msg | ||
// (with respect to the associated power table) and a non-nil error otherwise. | ||
VerifyAggSig(msg []byte, sig []byte) error |
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 mentioned, I think there would be benefits from not embedding together list of signers and aggregated signature into the sig []byte
.
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.
That would be very surprising! Surely a verification API must take the public keys as parameter.
// Verifies a signature for the given sender ID. | ||
Verify(sender ActorID, msg, sig []byte) bool | ||
Verify(sender ActorID, msg, sig []byte) error |
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.
Upstream changes have separating signing (which has implicit knowledge of key) from verification (pure stateless function which takes key as parameter). Please follow that pattern.
} | ||
|
||
// AggSignatureScheme encapsulates signature aggregation functionality. | ||
// The implementation is expected to be aware of a mapping between ActorIDs and public keys. |
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 above, I don't think we want this implicit awareness. The API should be purely in terms of keys.
// as this AggSignatureScheme, otherwise the behavior of VerifyAggSig is undefined. | ||
// VerifyAggSig returns nil if sig is a valid aggregate signature of msg | ||
// (with respect to the associated power table) and a non-nil error otherwise. | ||
VerifyAggSig(msg []byte, sig []byte) error |
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.
That would be very surprising! Surely a verification API must take the public keys as parameter.
|
||
// SigAggregator holds the intermediate state of signature aggregation. | ||
// Use one aggregator per aggregated signature. | ||
type SigAggregator interface { |
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 far as I can tell at the moment, we have no need for incremental accumulation of signers or intermediate state. Just make a simple pure function that takes a list of signatures and keys as parameters. We should spec the simplest API here that does what f3 needs, not what the underlying BLS library could potentially do.
@@ -325,11 +327,14 @@ func (i *instance) isJustified(msg *GMessage) bool { | |||
} | |||
|
|||
// Sends this node's QUALITY message and begins the QUALITY phase. | |||
func (i *instance) beginQuality() { | |||
func (i *instance) beginQuality() error { |
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.
Discussion in #11 will have some bearing here. I am not keen on propagating error returns here for internal invariant problems. Maybe this will go away once the signing API is purely in terms of keys though?
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 think it came up for me with broadcast failing, which is not invariant problem.
Looks like this PR needs to be rebased on top of upstream changes. The new upstream signing API is still different, but it does make sense to me. It should not be hard to modify the The Regarding the signatures on G2, I guess we can do one of
What do you think? |
I have started a new branch based on your code. My plan is to begin with signatures on G1 and make it easy to switch. |
Ok, let me know if I can be of any help there. |
Suberceeded by #70 |
This is a proposition of an API for the aggregated signatures. In a nutshell, each participant would have an instance of a signature scheme, initialized with its own private key and the power table.
In addition to simple signing and verifying other participant's signatures, the scheme can produce and verify aggregated signatures. Each aggregated signature carries information about the identities of the participants whose signatures it aggregates.
WDYT about removing the
Signer
interface from thehost
and using a separateAggSignatureScheme
that can be passed to the participant the same way as theVRFer
is?