Skip to content
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

refactor!: don't send public key with signature #4518

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

mversic
Copy link
Contributor

@mversic mversic commented Apr 26, 2024

Description

Linked issue

Closes #4393 #4410

Benefits

Checklist

  • I've read CONTRIBUTING.md
  • I've used the standard signed-off commit format (or will squash just before merging)
  • All applicable CI checks pass (or I promised to make them pass later)
  • (optional) I've written unit tests for the code changes
  • I replied to all comments after code review, marking all implemented changes with thumbs up

@github-actions github-actions bot added iroha2-dev The re-implementation of a BFT hyperledger in RUST Refactor Improvement to overall code quality labels Apr 26, 2024
@mversic mversic force-pushed the remove_public_key branch 2 times, most recently from cee8760 to df420c1 Compare April 26, 2024 14:02
@github-actions github-actions bot added the api-changes Changes in the API for client libraries label Apr 26, 2024
@mversic mversic changed the title [refactor] #4393: don't send public key with signature (refactor): don't send public key with signature May 1, 2024
@mversic mversic changed the title (refactor): don't send public key with signature refactor!: don't send public key with signature May 1, 2024
@mversic mversic force-pushed the remove_public_key branch 2 times, most recently from dc4d7d7 to 3701fc5 Compare May 2, 2024 07:52
@mversic mversic removed the iroha2-dev The re-implementation of a BFT hyperledger in RUST label May 2, 2024
data_model/src/block.rs Outdated Show resolved Hide resolved
data_model/src/block.rs Outdated Show resolved Hide resolved
core/src/sumeragi/network_topology.rs Outdated Show resolved Hide resolved
core/src/block.rs Show resolved Hide resolved
@mversic mversic force-pushed the remove_public_key branch 5 times, most recently from bdb903e to fb852e9 Compare May 8, 2024 10:42
@mversic mversic force-pushed the remove_public_key branch 2 times, most recently from d1c63f2 to 9edd012 Compare May 22, 2024 06:50
client/src/config/user/boilerplate.rs Outdated Show resolved Hide resolved
core/src/smartcontracts/isi/query.rs Show resolved Hide resolved
Comment on lines +269 to +275
if additional_leader_signatures.next().is_some() {
return Err(SignatureVerificationError::DuplicateSignatures {
signatory: leader_index,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have concern about this check, can't malicious peer insert wrong index and valid signature (for random key pair)?
So that it looks like leader submitted multiple signatures.

This way malicous peer can't prevent safety of blockchain, but can violate liveliness.

Maybe we can require that at least one signature should be from leader?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

malicious peer can send all sorts of invalid blocks. What you're saying is true, but is no less dangerous than sending other types of invalid blocks. We just reject a block sent from this peer, there is no harm in that IMO

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what @Erigara is saying is that the block validation will globally and repeatedly fail unless it has a mechanism to identify and ignore (or expel from the validator set) the peer that spoof index zero with its signature.
The problem here would be trusting the index without checking it against the signature

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what @Erigara is saying is that the block validation will globally and repeatedly fail unless it has a mechanism to identify and ignore (or expel from the validator set) the peer that spoof index zero with its signature.

spoofing 0 index is just one way in which malicious peer can affect consensus. Worst case, malicious peer can withhold sending any messages to other peers. It will only fail consensus repeatedly until the malicious peer becomes observing peer. 0 index spoofing is no special case and I don't think we should be more forgiving in this case

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, at least this is not a problem specific to this PR. It used to trust public keys instead of indices

core/src/block.rs Outdated Show resolved Hide resolved
core/src/tx.rs Outdated Show resolved Hide resolved
core/src/sumeragi/network_topology.rs Show resolved Hide resolved
core/src/sumeragi/network_topology.rs Show resolved Hide resolved
core/src/sumeragi/view_change.rs Outdated Show resolved Hide resolved
data_model/src/query/mod.rs Outdated Show resolved Hide resolved
data_model/src/block.rs Outdated Show resolved Hide resolved
@mversic mversic force-pushed the remove_public_key branch 6 times, most recently from 211e87a to ad1aa08 Compare May 23, 2024 18:46
Comment on lines +632 to 662
(BlockMessage::BlockSigned(BlockSigned { signatures }), Role::ProxyTail) => {
info!(
peer_id=%self.peer_id,
role=%self.role(),
"Received block signatures"
);

let roles: &[Role] = if current_view_change_index >= 1 {
let roles: &[Role] = if view_change_index >= 1 {
&[Role::ValidatingPeer, Role::ObservingPeer]
} else {
&[Role::ValidatingPeer]
};
let valid_signatures =
current_topology.filter_signatures_by_roles(roles, &signatures);

if let Some(voted_block) = voting_block.as_mut() {
let voting_block_hash = voted_block.block.as_ref().hash_of_payload();

if hash == voting_block_hash {
add_signatures::<true>(voted_block, valid_signatures);
} else {
debug!(%voting_block_hash, "Received signatures are not for the current block");
}
let valid_signatures = self
.topology
.filter_signatures_by_roles(roles, &signatures)
.cloned();

if let Some(mut voted_block) = voting_block.take() {
add_signatures::<true>(&mut voted_block, valid_signatures, &self.topology);
*voting_block = self.try_commit_block(voted_block, is_genesis_peer);
} else {
// NOTE: Due to the nature of distributed systems, signatures can sometimes be received before
// the block (sent by the leader). Collect the signatures and wait for the block to be received
voting_signatures.extend(valid_signatures);
}
}
Copy link
Contributor

@Erigara Erigara May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By looking at this code and try_commit it look like we don't check that signature indeed belong to the peer.
So any peer (including malicious leader itself) can send BlockSigned message with random block matching signatures for enough validating/observing peers and block will be committed.

Another problem is that on this path signatures are not checked for duplication.
So as before peer can submit BlockSigned message with the same index and signature and block will be committed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. ValidBlock is guaranteed to have all valid signatures except for the ProxyTail signature
  2. valid_bloc.commit will check that ProxyTail signature is valid
  3. try_commit is only invoked by the ProxyTail. You can see there is an assert statement inside

Comment on lines +741 to +750
.block
.commit(&self.topology)
.unpack(|e| self.send_event(e))
.expect("INTERNAL BUG: Proxy tail failed to commit block");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of unwrapping we can try to commit the block in the if statement?
I think it would be more reliable in long term if we decide that to commit block it's not enough to just receive enough signatures.
And proxy tail can add it's signature on BlockCreated message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function is only callable by the ProxyTail and ValidBlock is guaranteed to have all signatures valid. Consequently, this cannot and must not fail unless the ProxyTail is itself faulty

Comment on lines +567 to 571
if let Err(err) = Self::verify_proxy_tail_signature(self.as_ref(), topology) {
return WithEvents::new(Err((self, err.into())));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably should check that whole set of sigantures is valid.

Copy link
Contributor Author

@mversic mversic May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValidBlock is guaranteed to have all signatures valid except ProxyTail

@mversic mversic force-pushed the remove_public_key branch 4 times, most recently from e366a7d to 434750f Compare May 27, 2024 07:28
@mversic mversic requested review from Erigara and s8sato May 27, 2024 08:08
Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
@mversic mversic force-pushed the remove_public_key branch 4 times, most recently from ee215e4 to 40f7083 Compare May 27, 2024 12:05
Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-changes Changes in the API for client libraries Refactor Improvement to overall code quality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Should we pack public keys with signatures?
3 participants