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: [...]}
-
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)
Security Issue: Signature Scope Attack (QC View Not Protected)
Summary
A critical security vulnerability exists in
VerifyQuorumCertthat 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:
Vulnerability Details
Root Cause
In
security/cert/auth.go, theVerifyQuorumCertfunction only validates signatures againstblock.ToBytes():While
block.ToBytes()includesblock.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
QC_Old = {View: 10, BlockHash: Hash(A), Signatures: [...]}Attack: Byzantine node extracts signatures and creates a forged QC
Injection: Attacker constructs a "time-warp" proposal for View 101
Impact: Nodes accept the fake QC because:
Concrete Attack Consequences
Proof of Concept
The vulnerability has been verified across all three cryptographic implementations:
Affected Versions
All versions of the HotStuff implementation are affected.
Recommended Fix
Add a simple validation check in
VerifyQuorumCert:Why This Fix Is Safe
QC.View == Block.ViewReferences
Timeline
fix/signature-scope-attack)