-
Notifications
You must be signed in to change notification settings - Fork 68
Description
Security Issue: Signature Scope Attack (QC View Not Protected)
Summary
A critical security vulnerability exists in VerifyQuorumCert that allows attackers to forge QuorumCertificates (QCs) with arbitrary view numbers by replaying signatures from legitimate QCs.
Severity
Critical - This vulnerability can be exploited by Byzantine nodes to:
- Manipulate consensus view progression
- Forge "time-warp" proposals
- Disrupt liveness guarantees
- Potentially cause safety violations under certain conditions
Vulnerability Details
Root Cause
In security/cert/auth.go, the VerifyQuorumCert function only validates signatures against block.ToBytes():
func (c *Authority) VerifyQuorumCert(qc hotstuff.QuorumCert) error {
// ... checks ...
block, ok := c.blockchain.Get(qc.BlockHash())
// ...
return c.Verify(qc.Signature(), block.ToBytes()) // ← QC.View is NOT verified!
}While block.ToBytes() includes block.View, the QC's View field is stored separately and is not cryptographically bound to the signature.
Attack Scenario: The Signature Transplant Attack
-
Setup: Cluster runs normally, achieving consensus at View 10 for Block A
- A legitimate QC exists:
QC_Old = {View: 10, BlockHash: Hash(A), Signatures: [...]}
- A legitimate QC exists:
-
Attack: Byzantine node extracts signatures and creates a forged QC
QC_Fake = { View: 100, // ← TAMPERED (was 10) BlockHash: Hash(A), // Same as original Signatures: [...] // Copied from QC_Old } -
Injection: Attacker constructs a "time-warp" proposal for View 101
- Parent: Block A
- Justification: QC_Fake (claiming View 100 consensus)
-
Impact: Nodes accept the fake QC because:
- Signatures are valid (they were created for Block A)
- Block A exists in blockchain
- QC.View field is never verified against Block.View
Concrete Attack Consequences
| Attack Vector | Impact |
|---|---|
| HighQC Manipulation | Artificially inflate HighQC.View to arbitrary values (e.g., 999) |
| View Jumping | Force nodes to skip legitimate view progression |
| Liveness Attack | Cause consensus stalls by disrupting view synchronization |
| Proposal Forgery | Create valid-looking proposals for future views |
Proof of Concept
The vulnerability has been verified across all three cryptographic implementations:
=== RUN TestSignatureScopeAttack_QCViewNotSigned/ecdsa
🚨 SIGNATURE SCOPE ATTACK SUCCESSFUL!
Original QC View: 10
Fake QC View: 100 (TAMPERED)
Verification: PASSED ← Should have FAILED!
=== RUN TestSignatureScopeAttack_QCViewNotSigned/eddsa
🚨 SIGNATURE SCOPE ATTACK SUCCESSFUL!
=== RUN TestSignatureScopeAttack_QCViewNotSigned/bls12
🚨 SIGNATURE SCOPE ATTACK SUCCESSFUL!
Affected Versions
All versions of the HotStuff implementation are affected.
Recommended Fix
Add a simple validation check in VerifyQuorumCert:
func (c *Authority) VerifyQuorumCert(qc hotstuff.QuorumCert) error {
// ... existing checks ...
block, ok := c.blockchain.Get(qc.BlockHash())
if !ok {
return fmt.Errorf("block not found: %v", qc.BlockHash())
}
// NEW: Prevent signature replay attacks
if qc.View() != block.View() {
return fmt.Errorf("QC view %d does not match block view %d (possible signature replay attack)",
qc.View(), block.View())
}
return c.Verify(qc.Signature(), block.ToBytes())
}Why This Fix Is Safe
| Concern | Analysis |
|---|---|
| Performance | O(1) integer comparison - negligible impact |
| Backward Compatibility | No signature format changes |
| Network Protocol | No protocol changes required |
| Existing Code | All legitimate QCs already satisfy QC.View == Block.View |
References
- HotStuff Paper: Section on QC verification
- Related: View synchronization in BFT protocols
Timeline
- Discovered: 2025-11-30
- Reported: 2025-11-30
- Fix Available: 2025-11-30 (branch
fix/signature-scope-attack)