Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
5e9f2ef
Initial plan
Copilot Oct 17, 2025
8674212
Upgrade ECDSA to use modern ASN.1 methods and add comprehensive tests
Copilot Oct 17, 2025
b85b693
Address code review feedback - add documentation and clarifications
Copilot Oct 17, 2025
8f753a5
Final verification - all tests pass and security checks complete
Copilot Oct 17, 2025
de93c22
Address review feedback: simplify ECDSA signature to use raw ASN.1 bytes
Copilot Oct 17, 2025
1a002ca
Merge branch 'package-restructuring' into copilot/upgrade-crypto-libr…
meling Oct 19, 2025
cc492ad
fix(ecdsa_test): adjust Multi signature construction after merge
meling Oct 19, 2025
b2e81dc
chore(crypto): use for range loops and b.Loop()
meling Oct 19, 2025
9241421
refactor(ecdsa, eddsa): use NewMulti to create new signature
meling Oct 19, 2025
f87c028
chore(ecdsa_test): simplify test case structure, add err check
meling Oct 19, 2025
a0ef5a9
refactor(ecdsa_test): add test helper for multi ecdsa signers
meling Oct 19, 2025
e6c87b2
refactor(ecdsa_test): simplify signature restore/construction test
meling Oct 19, 2025
366b69c
fix(crypto): bug in errors.Join() usage
meling Oct 19, 2025
7f9cd69
feat(ecdsa_test): expand test coverage in TestBatchVerify
meling Oct 19, 2025
01cc72c
feat(ecdsa_test): expand test coverage in TestECDSACombine
meling Oct 19, 2025
797bb9d
feat(ecdsa_test): add test case for unordered signature combination
meling Oct 19, 2025
cb8b88d
refactor(ecdsa_test): replace setup func w/testutil.CreateSigners
meling Oct 19, 2025
865760f
feat(hotstuffpb): add test for combining unordered ECDSA signatures
meling Oct 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,6 @@ github.com/relab/container v0.0.0-20250707110336-0e5fd78d9eb4 h1:GhAEx4eVZzERj3D
github.com/relab/container v0.0.0-20250707110336-0e5fd78d9eb4/go.mod h1:oLZXG1NirJWzF2fMEeMUC6OLiMn6RtCChzUz1jtF/qs=
github.com/relab/gorums v0.7.1-0.20220818130557-8533cb369cd6 h1:azahqG2RhvhFvHiJ5JLhlX8+vViIVe4ZSD4VryHYvfE=
github.com/relab/gorums v0.7.1-0.20220818130557-8533cb369cd6/go.mod h1:dS1JU8uB1QgQie2bvRPeJWWmIFLPyl5IU50YfWpYVBE=
github.com/relab/iago v0.0.0-20250710222014-f7912c9bce3b h1:YG8NMVpHyT5zVk8Y3+Xxp/q1OsrV+JH17rqQt0QUc9c=
github.com/relab/iago v0.0.0-20250710222014-f7912c9bce3b/go.mod h1:v6bbUgeQQmcgpPGPxHpNyFIHtOje+kLUvDQQGLiO9Tw=
github.com/relab/iago v0.0.0-20250711112846-29626d6ceae7 h1:PWlBuuELP0oqOJ/p92JiUu4z89LBKI4rS9dY35h/JLY=
github.com/relab/iago v0.0.0-20250711112846-29626d6ceae7/go.mod h1:v6bbUgeQQmcgpPGPxHpNyFIHtOje+kLUvDQQGLiO9Tw=
github.com/relab/iago v0.0.0-20250711130143-c9801aedd85d h1:hT/hiVbXlWURqYNU9iYnZ1urPfzb7IVmgd40YAHJ/p4=
github.com/relab/iago v0.0.0-20250711130143-c9801aedd85d/go.mod h1:v6bbUgeQQmcgpPGPxHpNyFIHtOje+kLUvDQQGLiO9Tw=
github.com/relab/wrfs v0.0.0-20220416082020-a641cd350078 h1:JN5qn8C/HZoyMAycX6z6O0SeX+09CV3w3GcVNW70OZA=
Expand Down Expand Up @@ -241,8 +237,6 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down Expand Up @@ -297,8 +291,6 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand All @@ -325,8 +317,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
Expand All @@ -338,8 +328,6 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -353,8 +341,6 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
Expand Down
70 changes: 26 additions & 44 deletions internal/proto/clientpb/client.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 2 additions & 9 deletions internal/proto/hotstuffpb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
package hotstuffpb

import (
"math/big"

"github.com/relab/hotstuff"
"github.com/relab/hotstuff/security/crypto"
"google.golang.org/protobuf/types/known/timestamppb"
Expand All @@ -18,8 +16,7 @@ func QuorumSignatureToProto(sig hotstuff.QuorumSignature) *QuorumSignature {
for _, s := range ms {
sigs = append(sigs, &ECDSASignature{
Signer: uint32(s.Signer()),
R: s.R().Bytes(),
S: s.S().Bytes(),
Sig: s.ToBytes(),
})
}
signature.Sig = &QuorumSignature_ECDSASigs{ECDSASigs: &ECDSAMultiSignature{
Expand Down Expand Up @@ -49,11 +46,7 @@ func QuorumSignatureFromProto(sig *QuorumSignature) hotstuff.QuorumSignature {
if signature := sig.GetECDSASigs(); signature != nil {
sigs := make([]*crypto.ECDSASignature, len(signature.GetSigs()))
for i, sig := range signature.GetSigs() {
r := new(big.Int)
r.SetBytes(sig.GetR())
s := new(big.Int)
s.SetBytes(sig.GetS())
sigs[i] = crypto.RestoreECDSASignature(r, s, hotstuff.ID(sig.GetSigner()))
sigs[i] = crypto.RestoreECDSASignature(sig.GetSig(), hotstuff.ID(sig.GetSigner()))
}
return crypto.NewMulti(sigs...)
}
Expand Down
36 changes: 36 additions & 0 deletions internal/proto/hotstuffpb/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,39 @@ func TestTimeoutMsgFromProto_Issue129(t *testing.T) {
})
}
}

func TestQuorumSignatureFromProto_UnorderedECDSA(t *testing.T) {
signers := testutil.CreateSigners[*crypto.ECDSA](t, 5)

message := []byte("combine message")
// Create signatures in an unordered fashion: [3,2,1,0,4]
indices := []int{3, 2, 1, 0, 4}
sigs := make([]hotstuff.QuorumSignature, len(indices))
for i, idx := range indices {
sig, err := signers[idx].Sign(message)
if err != nil {
t.Fatalf("Sign failed for replica %d: %v", idx+1, err)
}
sigs[i] = sig
}

// Combine signatures into a Multi signature
combined, err := signers[0].Combine(sigs...)
if err != nil {
t.Fatalf("Combine failed: %v", err)
}

// Convert to protobuf and back (simulate network transfer)
protoSig := hotstuffpb.QuorumSignatureToProto(combined)
restored := hotstuffpb.QuorumSignatureFromProto(protoSig)

// Should be able to verify the restored signature
if err := signers[0].Verify(restored, message); err != nil {
t.Fatalf("Verify restored signature failed: %v", err)
}

// Check that the restored signature has the correct number of participants
if restored.Participants().Len() != len(indices) {
t.Fatalf("expected %d participants, got %d", len(indices), restored.Participants().Len())
}
}
Loading