-
Notifications
You must be signed in to change notification settings - Fork 104
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
Check block integrity when necessary #1764
Conversation
I think this covers the different request of blocks we do in the Java library. @kc1212 Please let me know if I forgot something. I'm investigating the skipchain service on the Go side.. |
The java side looks good |
10b0618
to
83475a0
Compare
This fixes the Java and Go verification of a proof to add a check for the last forward link to point on the actual hash of the latest block to be sure the latest block has not been tempered. Fixes #1703
This adds a check to the skipchain rpc when asking for a skipblock so that the hash must match the block identifier Fixes #1703
This adds block integrity checks among the skipchain service when blocks are requested from a peer which means it could be malicious data. Fixes #1703
This adds further checkings in byzcoin and skipchain services so that a request to a distant node is verified before usage.
The value returned must be assumed untrusted because the service is lacking the context to verify it belongs to the chain. Verifications must be done upstream.
8c8057a
to
dd4195b
Compare
This adds a verification step to make sure the returned genesis block has been created with the same proposed parameters
So the conclusion of this PR is that mainly the byzcoin and skipchain API were assuming data coming from the service is safe and then I added integrity checks where it was possible to verify. Sometimes you lack the context and then the verification must be done upstream, like when you ask for a single block (maybe we should always return a proof alongside?). Aside of that the main goal of this PR was to fix the proof verification to insure the last forward link points to the correct returned latest block. |
byzcoin/api.go
Outdated
func (c *Client) StreamTransactions(handler func(StreamingResponse, error)) error { | ||
req := StreamingRequest{ | ||
ID: c.ID, | ||
} | ||
// TODO: what if the leader is down ? |
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.
the error handler handler(StreamingResponse{}, err)
should be called and the function returns
// for testing purpose | ||
if testCorruptNewLedgerResponse != nil { | ||
reply = testCorruptNewLedgerResponse | ||
} |
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's easier to just test verifyGenesisBlock
by itself. then you don't need to insert test code into production code.
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.
another possibility: can we register a corrupted byzcoin service that always just returns a wrong genesis block (e.g., embed byzcoin.Service
inside byzcoin.BadService
and overwrite the create genesis block function), it doesn't even have to do consensus or anything like that, just returns the bad block? just a few ideas to think about
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.
Yes for the verifyGenesisBlock
but I really want to make sure the request is checking this, and that no one removed this check accidentally.
another posibility...
I tried that first, but the service name is a constant (which is fine) so you can't change which service is going to be used, or am I missing something ? I would love to do it that way..
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.
Nevermind, I found a way to go around without too many complex mocking
byzcoin/api.go
Outdated
// send the block only if the integrity is correct | ||
handler(resp, nil) | ||
} else { | ||
log.Warnf("got a corrupted block from %v", c.Roster.List[0]) |
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.
probably you should call the handler here too, to say there is an error, like handler(StreamingResponse{}, err)
byzcoin/api.go
Outdated
@@ -417,3 +450,54 @@ func DefaultGenesisMsg(v Version, r *onet.Roster, rules []string, ids ...darc.Id | |||
} | |||
return &m, nil | |||
} | |||
|
|||
func verifyGenesisBlock(sb *skipchain.SkipBlock, req *CreateGenesisBlock) 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.
nit: I would name those parameters like: actual *skipchain.SkipBlock, expected *CreateGenesisBlock
, so it's obvious which input is trusted.
byzcoin/api.go
Outdated
} | ||
|
||
if len(data.TxResults) != 1 || len(data.TxResults[0].ClientTransaction.Instructions) != 1 { | ||
return nil, errors.New("didn't get only one instruction") |
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.
return nil, errors.New("didn't get only one instruction") | |
return nil, errors.New("genesis darc tx should only have one instruction") |
// we don't want a malicious peer to send a block with tempered | ||
// data like a different roster with different public keys | ||
throw new CothorityCommunicationException("invalid block integrity"); | ||
} |
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.
the function comment says the block is not verified, is that still the case?
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'll change the comment. The integrity is verified but the caller must verify if it belongs to the chain.
skipchain/api.go
Outdated
|
||
if testCorruptSSBResponse != nil { | ||
sb = testCorruptSSBResponse | ||
} |
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.
see comment above regarding the possibility of creating a bad service
return sb.Latest, nil | ||
} | ||
|
||
// CompareGenesisBlocks compares the content of two blocks and returns an error | ||
// if there is any difference | ||
func compareGenesisBlocks(prop *SkipBlock, ret *SkipBlock) 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.
why can't you re-calculate the hash? if the hash is different then the block content should also be different right?
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 wish I could but the service is adding information to the genesis like a random backlink
The proof verification was not checking that the last forward link was pointing on the actual latest block provided by the proof which that a malicious peer can in some extend modify the block. This check has been added in the Java and the Go code.
Another issue was that only the forward links of a request skipblock were checked so any other fields could be modified as we were not checking the received skipblock hash. This has been fixed in the Java library.
Fixes #1703