diff --git a/server_mock_test.go b/server_mock_test.go index 0e8c029b..e8bc4727 100644 --- a/server_mock_test.go +++ b/server_mock_test.go @@ -6,12 +6,14 @@ import ( "testing" "time" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/lndclient" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/test" + "github.com/lightningnetwork/lnd/input" invpkg "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" @@ -32,6 +34,13 @@ var ( testMaxSwapAmount = btcutil.Amount(1000000) ) +// mockMuSig2SigningData returns size-correct placeholder data. Client tests +// only assert response handling, not MuSig2 cryptographic validity. +func mockMuSig2SigningData() ([]byte, []byte, error) { + return make([]byte, musig2.PubNonceSize), + make([]byte, input.MuSig2PartialSigSize), nil +} + // serverMock is used in client unit tests to simulate swap server behaviour. type serverMock struct { expectedSwapAmt btcutil.Amount @@ -276,7 +285,7 @@ func (s *serverMock) MuSig2SignSweep(_ context.Context, _ loopdb.ProtocolVersion _ lntypes.Hash, _ [32]byte, _ []byte, _ []byte) ([]byte, []byte, error) { - return nil, nil, nil + return mockMuSig2SigningData() } func (s *serverMock) MultiMuSig2SignSweep(ctx context.Context, @@ -285,7 +294,7 @@ func (s *serverMock) MultiMuSig2SignSweep(ctx context.Context, prevoutMap map[wire.OutPoint]*wire.TxOut) ( []byte, []byte, error) { - return nil, nil, nil + return mockMuSig2SigningData() } func (s *serverMock) PushKey(_ context.Context, _ loopdb.ProtocolVersion, diff --git a/swap_server_client.go b/swap_server_client.go index fcec06be..16022664 100644 --- a/swap_server_client.go +++ b/swap_server_client.go @@ -405,13 +405,9 @@ func (s *grpcSwapServerClient) NewLoopOutSwap(ctx context.Context, return nil, err } - var senderKey [33]byte - copy(senderKey[:], swapResp.SenderKey) - - // Validate sender key. - _, err = btcec.ParsePubKey(senderKey[:]) + senderKey, err := parseServerPubKey("sender key", swapResp.SenderKey) if err != nil { - return nil, fmt.Errorf("invalid sender key: %v", err) + return nil, err } return &newLoopOutResponse{ @@ -470,14 +466,22 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context, return nil, err } - var receiverKey, receiverInternalKey [33]byte - copy(receiverKey[:], swapResp.ReceiverKey) - copy(receiverInternalKey[:], swapResp.ReceiverInternalPubkey) - - // Validate receiver key. - _, err = btcec.ParsePubKey(receiverKey[:]) + receiverKey, err := parseServerPubKey( + "receiver key", swapResp.ReceiverKey, + ) if err != nil { - return nil, fmt.Errorf("invalid sender key: %v", err) + return nil, err + } + + var receiverInternalKey [btcec.PubKeyBytesLenCompressed]byte + if loopdb.CurrentProtocolVersion() >= loopdb.ProtocolVersionMuSig2 { + receiverInternalKey, err = parseServerPubKey( + "receiver internal key", + swapResp.ReceiverInternalPubkey, + ) + if err != nil { + return nil, err + } } return &newLoopInResponse{ @@ -488,6 +492,29 @@ func (s *grpcSwapServerClient) NewLoopInSwap(ctx context.Context, }, nil } +// parseServerPubKey validates that keyBytes is a well-formed compressed public +// key received from the server and returns it as a fixed-size array. The name +// argument is used to produce a descriptive error if validation fails. +func parseServerPubKey(name string, + keyBytes []byte) ([btcec.PubKeyBytesLenCompressed]byte, error) { + + var key [btcec.PubKeyBytesLenCompressed]byte + + if len(keyBytes) != btcec.PubKeyBytesLenCompressed { + return key, fmt.Errorf("invalid %s length: got %d, want %d", + name, len(keyBytes), btcec.PubKeyBytesLenCompressed) + } + + _, err := btcec.ParsePubKey(keyBytes) + if err != nil { + return key, fmt.Errorf("invalid %s: %v", name, err) + } + + copy(key[:], keyBytes) + + return key, nil +} + // ServerUpdate summarizes an update from the swap server. type ServerUpdate struct { // State is the state that the server has sent us. diff --git a/swap_server_client_test.go b/swap_server_client_test.go new file mode 100644 index 00000000..81826f8c --- /dev/null +++ b/swap_server_client_test.go @@ -0,0 +1,28 @@ +package loop + +import ( + "testing" + + looptest "github.com/lightninglabs/loop/test" + "github.com/stretchr/testify/require" +) + +// TestParseServerPubKey ensures that parseServerPubKey accepts a valid +// compressed public key and rejects keys with an invalid length or contents. +func TestParseServerPubKey(t *testing.T) { + t.Parallel() + + _, pubKey := looptest.CreateKey(1) + pubKeyBytes := pubKey.SerializeCompressed() + + parsedKey, err := parseServerPubKey("test key", pubKeyBytes) + require.NoError(t, err) + require.Equal(t, pubKeyBytes, parsedKey[:]) + + _, err = parseServerPubKey("test key", pubKeyBytes[:32]) + require.ErrorContains(t, err, "invalid test key length") + + invalidKey := make([]byte, 33) + _, err = parseServerPubKey("test key", invalidKey) + require.ErrorContains(t, err, "invalid test key") +} diff --git a/testcontext_test.go b/testcontext_test.go index e9fe20cb..04fb6ce9 100644 --- a/testcontext_test.go +++ b/testcontext_test.go @@ -67,7 +67,7 @@ func mockMuSig2SignSweep(ctx context.Context, prevoutMap map[wire.OutPoint]*wire.TxOut) ( []byte, []byte, error) { - return nil, nil, nil + return mockMuSig2SigningData() } func newSwapClient(t *testing.T, config *clientConfig) *Client {