Skip to content

Commit 79c7dec

Browse files
committed
test: move setup decoder tests to utils_test and cover both symmetrically (F-2026-18199)
1 parent d4e86bd commit 79c7dec

2 files changed

Lines changed: 107 additions & 54 deletions

File tree

universalClient/tss/dkls/sign_test.go

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package dkls
22

33
import (
4-
"bytes"
54
"strings"
65
"testing"
76

@@ -131,56 +130,3 @@ func TestSignSession_EndToEnd(t *testing.T) {
131130
t.Errorf("expected 2 participants, got %d", len(result.Participants))
132131
}
133132
}
134-
135-
// DKLS signs the hash embedded in the setup blob, not the hash a follower
136-
// verified separately. SetupMessageHash exposes the embedded one so callers can
137-
// bind the two, which is what stops a coordinator presenting one hash for
138-
// verification and embedding another in the setup it distributes.
139-
func TestSetupMessageHash(t *testing.T) {
140-
participantIDs := encodeParticipantIDs([]string{"party1", "party2"})
141-
keyID := make([]byte, 32)
142-
143-
legitHash := make([]byte, 32)
144-
copy(legitHash, "legitimate-outbound-hash-32bytes")
145-
attackerHash := make([]byte, 32)
146-
copy(attackerHash, "attacker-chosen-vault-call-digest")
147-
148-
t.Run("returns the hash embedded in the setup", func(t *testing.T) {
149-
setup, err := session.DklsSignSetupMsgNew(keyID, nil, legitHash, participantIDs)
150-
if err != nil {
151-
t.Fatalf("failed to build sign setup: %v", err)
152-
}
153-
got, err := SetupMessageHash(setup)
154-
if err != nil {
155-
t.Fatalf("SetupMessageHash() error = %v", err)
156-
}
157-
if !bytes.Equal(got, legitHash) {
158-
t.Errorf("SetupMessageHash() = %x, want %x", got, legitHash)
159-
}
160-
})
161-
162-
// The attack: a setup built over attackerHash must not report legitHash, so
163-
// a caller comparing against its verified hash detects the substitution.
164-
t.Run("substituted setup reports the attacker hash", func(t *testing.T) {
165-
setup, err := session.DklsSignSetupMsgNew(keyID, nil, attackerHash, participantIDs)
166-
if err != nil {
167-
t.Fatalf("failed to build sign setup: %v", err)
168-
}
169-
got, err := SetupMessageHash(setup)
170-
if err != nil {
171-
t.Fatalf("SetupMessageHash() error = %v", err)
172-
}
173-
if bytes.Equal(got, legitHash) {
174-
t.Fatal("substituted setup must not report the legitimate hash")
175-
}
176-
if !bytes.Equal(got, attackerHash) {
177-
t.Errorf("SetupMessageHash() = %x, want %x", got, attackerHash)
178-
}
179-
})
180-
181-
t.Run("rejects empty setup", func(t *testing.T) {
182-
if _, err := SetupMessageHash(nil); err == nil {
183-
t.Error("SetupMessageHash(nil) should error")
184-
}
185-
})
186-
}

universalClient/tss/dkls/utils_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package dkls
22

33
import (
4+
"bytes"
45
"crypto/sha256"
56
"testing"
7+
8+
session "go-wrapper/go-dkls/sessions"
69
)
710

811
func TestDeriveKeyID(t *testing.T) {
@@ -58,3 +61,107 @@ func TestEncodeParticipantIDs(t *testing.T) {
5861
})
5962
}
6063
}
64+
65+
// The setup blob is what DKLS actually runs on, so these decoders are what let a
66+
// follower bind it to the values it validated separately. Both must report what
67+
// the blob really contains, and must error rather than guess on a malformed one.
68+
69+
func TestSetupMessageHash(t *testing.T) {
70+
participantIDs := encodeParticipantIDs([]string{"party1", "party2"})
71+
keyID := make([]byte, 32)
72+
73+
legitHash := make([]byte, 32)
74+
copy(legitHash, "legitimate-outbound-hash-32bytes")
75+
attackerHash := make([]byte, 32)
76+
copy(attackerHash, "attacker-chosen-vault-call-digest")
77+
78+
t.Run("returns the hash embedded in the setup", func(t *testing.T) {
79+
setup, err := session.DklsSignSetupMsgNew(keyID, nil, legitHash, participantIDs)
80+
if err != nil {
81+
t.Fatalf("failed to build sign setup: %v", err)
82+
}
83+
got, err := SetupMessageHash(setup)
84+
if err != nil {
85+
t.Fatalf("SetupMessageHash() error = %v", err)
86+
}
87+
if !bytes.Equal(got, legitHash) {
88+
t.Errorf("SetupMessageHash() = %x, want %x", got, legitHash)
89+
}
90+
})
91+
92+
// A substituted setup must report the hash it really signs, which is what
93+
// makes the mismatch detectable.
94+
t.Run("substituted setup reports the attacker hash", func(t *testing.T) {
95+
setup, err := session.DklsSignSetupMsgNew(keyID, nil, attackerHash, participantIDs)
96+
if err != nil {
97+
t.Fatalf("failed to build sign setup: %v", err)
98+
}
99+
got, err := SetupMessageHash(setup)
100+
if err != nil {
101+
t.Fatalf("SetupMessageHash() error = %v", err)
102+
}
103+
if bytes.Equal(got, legitHash) {
104+
t.Fatal("substituted setup must not report the legitimate hash")
105+
}
106+
if !bytes.Equal(got, attackerHash) {
107+
t.Errorf("SetupMessageHash() = %x, want %x", got, attackerHash)
108+
}
109+
})
110+
111+
t.Run("errors on empty and malformed setup", func(t *testing.T) {
112+
if _, err := SetupMessageHash(nil); err == nil {
113+
t.Error("SetupMessageHash(nil) should error")
114+
}
115+
if _, err := SetupMessageHash([]byte("not-a-dkls-setup")); err == nil {
116+
t.Error("SetupMessageHash(malformed) should error")
117+
}
118+
})
119+
}
120+
121+
func TestSetupParticipants(t *testing.T) {
122+
t.Run("returns the participants in index order", func(t *testing.T) {
123+
want := []string{"alice", "bob", "carol"}
124+
setup, err := session.DklsKeygenSetupMsgNew(2, nil, encodeParticipantIDs(want))
125+
if err != nil {
126+
t.Fatalf("failed to build keygen setup: %v", err)
127+
}
128+
got, err := SetupParticipants(setup)
129+
if err != nil {
130+
t.Fatalf("SetupParticipants() error = %v", err)
131+
}
132+
if len(got) != len(want) {
133+
t.Fatalf("SetupParticipants() = %v, want %v", got, want)
134+
}
135+
for i := range want {
136+
if got[i] != want[i] {
137+
t.Errorf("participant %d = %q, want %q", i, got[i], want[i])
138+
}
139+
}
140+
})
141+
142+
// Enumeration terminates on the first empty name rather than an error, which
143+
// is the contract this relies on to find the end of the list.
144+
t.Run("terminates at the end of a two party list", func(t *testing.T) {
145+
want := []string{"first", "second"}
146+
setup, err := session.DklsKeygenSetupMsgNew(2, nil, encodeParticipantIDs(want))
147+
if err != nil {
148+
t.Fatalf("failed to build keygen setup: %v", err)
149+
}
150+
got, err := SetupParticipants(setup)
151+
if err != nil {
152+
t.Fatalf("SetupParticipants() error = %v", err)
153+
}
154+
if len(got) != 2 || got[0] != "first" || got[1] != "second" {
155+
t.Errorf("SetupParticipants() = %v, want %v", got, want)
156+
}
157+
})
158+
159+
t.Run("errors on empty and malformed setup", func(t *testing.T) {
160+
if _, err := SetupParticipants(nil); err == nil {
161+
t.Error("SetupParticipants(nil) should error")
162+
}
163+
if _, err := SetupParticipants([]byte("not-a-dkls-setup")); err == nil {
164+
t.Error("SetupParticipants(malformed) should error")
165+
}
166+
})
167+
}

0 commit comments

Comments
 (0)