Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 31 additions & 0 deletions pkg/identity/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"crypto/ed25519"
"crypto/rand"
"encoding/hex"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"strings"
"testing"

Expand Down Expand Up @@ -766,3 +768,32 @@ func TestAuthorizeInitiatorMessage_NilSignature(t *testing.T) {
t.Error("AuthorizeInitiatorMessage() should fail with nil signature")
}
}

func TestVerifyInitiatorMessage_DerivationPathBound(t *testing.T) {
pub, priv, _ := generateTestEd25519Key()

store := &fileStore{
initiatorKey: &InitiatorKey{
Algorithm: types.EventInitiatorKeyTypeEd25519,
Ed25519: pub,
},
}

msg := &types.SignTxMessage{
KeyType: types.KeyTypeSecp256k1,
WalletID: "w",
NetworkInternalCode: "ETH",
TxID: "tx",
Tx: []byte("d"),
DerivationPath: []uint32{44, 60, 0, 0, 0, 1},
}
raw, err := msg.Raw()
require.NoError(t, err)
msg.Signature = ed25519.Sign(priv, raw)

require.NoError(t, store.VerifyInitiatorMessage(msg), "should verify")

// tamper path
msg.DerivationPath = []uint32{44, 60, 0, 0, 0, 999}
assert.Error(t, store.VerifyInitiatorMessage(msg), "should not verify")
}
12 changes: 7 additions & 5 deletions pkg/types/initiator_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,19 @@ type ResharingMessage struct {
func (m *SignTxMessage) Raw() ([]byte, error) {
// omit the Signature field itself when computing the signed‐over data
payload := struct {
KeyType KeyType `json:"key_type"`
WalletID string `json:"wallet_id"`
NetworkInternalCode string `json:"network_internal_code"`
TxID string `json:"tx_id"`
Tx []byte `json:"tx"`
KeyType KeyType `json:"key_type"`
WalletID string `json:"wallet_id"`
NetworkInternalCode string `json:"network_internal_code"`
TxID string `json:"tx_id"`
Tx []byte `json:"tx"`
DerivationPath []uint32 `json:"derivation_path,omitempty"`
}{
KeyType: m.KeyType,
WalletID: m.WalletID,
NetworkInternalCode: m.NetworkInternalCode,
TxID: m.TxID,
Tx: m.Tx,
DerivationPath: m.DerivationPath,
}
return json.Marshal(payload)
}
Expand Down
48 changes: 48 additions & 0 deletions pkg/types/initiator_msg_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import (
"crypto/ed25519"
"crypto/rand"
"encoding/json"
"testing"

Expand Down Expand Up @@ -209,3 +211,49 @@ func TestGenerateKeyMessage_EmptyWallet(t *testing.T) {
assert.Equal(t, []byte(""), raw)
assert.Equal(t, "", msg.InitiatorID())
}

func TestSignTxMessage_Raw_IncludesDerivationPath(t *testing.T) {
msg := &SignTxMessage{
KeyType: KeyTypeSecp256k1,
WalletID: "wallet-123",
NetworkInternalCode: "BTC",
TxID: "tx-456",
Tx: []byte("transaction-data"),
Signature: []byte("signature-data"),
DerivationPath: []uint32{44, 60, 0, 0, 0, 1},
}
raw, err := msg.Raw()
require.NoError(t, err)
assert.NotEmpty(t, raw)
assert.Contains(t, string(raw), `"derivation_path":[44,60,0,0,0,1]`)
}

func TestSignTxMessage_DerivationPathIsSignatureBound(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)

msg := &SignTxMessage{
KeyType: KeyTypeSecp256k1,
WalletID: "w",
NetworkInternalCode: "ETH",
TxID: "tx",
Tx: []byte("d"),
DerivationPath: []uint32{44, 60, 0, 0, 1},
}

// initiator sign on Raw()
raw, err := msg.Raw()
require.NoError(t, err)
assert.NotEmpty(t, raw)
sig := ed25519.Sign(priv, raw)

// verify pass
rawOK, _ := msg.Raw()
assert.True(t, ed25519.Verify(pub, rawOK, sig), "original raw should be valid")

// Attacker: change derivation_path, same sig
msg.DerivationPath = []uint32{44, 60, 0, 0, 2}
rawTempered, err := msg.Raw()
require.NoError(t, err)
assert.False(t, ed25519.Verify(pub, rawTempered, sig), "changed derivation_path should invalidate sig")
}
Loading