Skip to content

multi: validate server-provided signing data and clean up comments#1148

Merged
hieblmi merged 7 commits into
lightninglabs:masterfrom
hieblmi:harden-musig2-handling
May 29, 2026
Merged

multi: validate server-provided signing data and clean up comments#1148
hieblmi merged 7 commits into
lightninglabs:masterfrom
hieblmi:harden-musig2-handling

Conversation

@hieblmi
Copy link
Copy Markdown
Collaborator

@hieblmi hieblmi commented May 27, 2026

This PR does:

  • Fix small comment and test wording typos across static address, liquidity, loopdb, and loopout tests.
  • Reject malformed server MuSig2 nonce/signature data in static address loop-in/withdrawal signing paths.
  • Reject malformed server MuSig2 cosign data in sweep batch cooperative signing.
  • Validate server-provided compressed public keys before storing them in swap contracts, including MuSig2 loop-in receiver internal keys.
  • Add regression coverage for malformed server signing data and public key parsing.

@hieblmi hieblmi requested a review from starius May 27, 2026 14:36
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request strengthens the security and robustness of the client by enforcing strict validation on all signing data and public keys received from the server. By ensuring that MuSig2 nonces, signatures, and public keys conform to expected formats, the client is better protected against potential errors or malicious inputs. The changes also include minor documentation and test cleanup to improve the overall quality of the codebase.

Highlights

  • Enhanced Input Validation: Implemented strict validation for server-provided MuSig2 nonces, signatures, and public keys to ensure data integrity before processing.
  • Improved Error Handling: Added explicit error checks and descriptive error messages for malformed signing data and invalid public keys received from the server.
  • Regression Testing: Introduced new test cases to verify that the client correctly rejects malformed server-provided data.
  • Codebase Cleanup: Corrected various typos in comments and test function names to improve documentation clarity and maintainability.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request focuses on improving MuSig2 signature and nonce validation, refactoring server public key parsing, and correcting various typos across the codebase. Specifically, it introduces strict length checks for MuSig2 nonces and partial signatures in both the withdrawal manager and sweep batcher, updates test mocks to return valid dummy signing data instead of nil values, and adds comprehensive unit tests for these validation paths. There are no review comments to address, so no additional feedback is provided.

@hieblmi
Copy link
Copy Markdown
Collaborator Author

hieblmi commented May 28, 2026

@claude review this

@claude
Copy link
Copy Markdown

claude Bot commented May 28, 2026

Code Review

PR Summary: This PR hardens MuSig2 handling by validating server-provided signing data (nonces, partial signatures, public keys) before use, and fixes several comment/typo issues across the codebase.


Overview

A solid security-hardening PR. The core change is rejecting malformed server data early — before passing it to the signer — in three distinct code paths:

  1. sweepbatcher/sweep_batch.go: Co-op sweep signing
  2. staticaddr/loopin/manager.go: Static address loop-in signing
  3. staticaddr/withdraw/manager.go: Static address withdrawal signing

Additionally, parseServerPubKey in swap_server_client.go consolidates and improves public key validation on server responses.


Positive Observations

  • Bug fix in error message (swap_server_client.go): The old code erroneously said "invalid sender key" when validating the receiver key in NewLoopInSwap. The new parseServerPubKey helper correctly uses the name argument in the error string.

  • Protocol-version-aware validation (swap_server_client.go:464): receiverInternalKey is only validated when >= ProtocolVersionMuSig2, correctly avoiding false failures for older protocol peers that will not send this field.

  • nil guard on sigAndNonce (staticaddr/withdraw/manager.go): The nil check before map access is a meaningful defensive addition — previously a nil map value would have caused a nil-pointer dereference inside the loop body.

  • Idiomatic var nonce declaration: Changing nonce := [musig2.PubNonceSize]byte{} to var nonce [musig2.PubNonceSize]byte is a minor but correct style improvement.


Issues and Suggestions

1. Missing negative tests for sweepbatcher.musig2sign length validation

The new guards in sweepbatcher/sweep_batch.go (checking serverNonce/serverSig lengths) have no dedicated regression tests. The mock changes prevent nil from reaching the new guards, but a wrong-length nonce or sig from the server would still be untested. Consider adding a test parallel to TestSignMusig2Tx_InvalidServerSigningInfo in sweep_batcher_test.go.

2. Missing negative test for staticaddr/loopin nonce validation

manager.go:handleLoopInSweepReq now uses byteSliceTo66ByteSlice to validate the server nonce, but there is no test exercising the error path (wrong-length nonce returned by the server mock). A short table-driven test in loopin/manager_test.go similar to the withdraw variant would round out the regression coverage.

3. Duplicated byteSliceTo66ByteSlice (pre-existing, but worth noting)

The function is defined independently in both staticaddr/loopin/actions.go and instantout/instantout.go (with slightly different return types: [musig2.PubNonceSize]byte vs [66]byte). This PR adds a third call site. A shared utility in e.g. staticutil or a top-level internal package would eliminate drift risk. Not a blocker for this PR, but worth a follow-up.

4. Zero-value mock nonce/sig may mask downstream signing failures

mockMuSig2SigningData() returns all-zero byte slices of the correct length. These pass the new length checks but are cryptographically invalid (a zero nonce is degenerate for MuSig2). This is acceptable as long as the test flow never actually calls MuSig2RegisterNonces or MuSig2Sign with this data in a way that requires a valid signature. A short comment on testMuSig2SigningData noting that it returns placeholder data would reduce future confusion.

5. Minor: parseServerPubKey copies bytes before validating

The function copies keyBytes into the [33]byte array, calls ParsePubKey, and on error discards the copy. Validating before copying avoids the intermediate allocation on the error path. Very minor nit — the current code is correct, just slightly wasteful on the error path.


Test Coverage

Code path Valid-data test Malformed-data test
parseServerPubKey yes (swap_server_client_test.go) yes
withdraw/signMusig2Tx nil/bad nonce/bad sig n/a yes (TestSignMusig2Tx_InvalidServerSigningInfo)
sweepbatcher/musig2sign bad nonce/sig length missing
loopin/handleLoopInSweepReq bad nonce missing

Typo/Comment Fixes

All correct and appreciated: "manger" to "manager", "dispach" to "dispatch", "dns" to "data source name", "sever" to "server" (x4), "cancelation" to "cancellation", "at wide" to "at large", "prepush" to "prepayment", "timout" to "timeout".


Summary

The security hardening is correct and well-targeted. The main gap is that two of the three new validation paths (sweepbatcher, loopin) lack negative-case tests. The rest of the code quality is good — errors are contextual, the helper is clean, and the protocol-version guard on receiverInternalKey is handled properly. Recommend merging after addressing the test coverage gaps (or opening a follow-up issue for them).

@hieblmi hieblmi force-pushed the harden-musig2-handling branch from b681f73 to 3045e82 Compare May 28, 2026 09:16
Copy link
Copy Markdown
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🎉

Thanks for fixing it!

Comment thread sweepbatcher/sweep_batch.go
Comment thread sweepbatcher/sweep_batcher_test.go
Comment thread staticaddr/loopin/manager_test.go
Comment thread swap_server_client.go Outdated
}, nil
}

func parseServerPubKey(name string, keyBytes []byte) ([33]byte, error) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add godoc

Comment thread swap_server_client.go Outdated
return nil, err
}

var receiverInternalKey [33]byte
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/33/btcec.PubKeyBytesLenCompressed/

Here and below

Comment thread swap_server_client_test.go
hieblmi added 3 commits May 29, 2026 11:53
Server-supplied nonces and partial signatures are consumed by the static address loop-in and withdrawal MuSig2 signing paths. Reject nil signing info, wrong nonce lengths, and wrong partial signature lengths before registering nonces or combining signatures, so malformed responses cannot be silently zero-padded into signing attempts.

Add withdrawal coverage for nil and malformed server signing data.
The cooperative batch sweep path receives a server nonce and partial signature before constructing a keyspend witness. Validate both byte slice lengths before registering the nonce or combining signatures, so malformed server responses fail explicitly instead of being zero-padded into fixed-size MuSig2 buffers.

Update batcher test helpers to return size-correct placeholder signing data under the stricter validation.
Loop-in and loop-out responses carry compressed server public keys that are copied into fixed-size fields and later used for HTLC construction. Validate the length and parse each compressed key before storing it, and validate the MuSig2 loop-in receiver internal key as well.

This turns short or unparsable server keys into explicit errors instead of silently zero-padding short responses or accepting an invalid internal key. Update root test mocks to return size-correct MuSig2 signing data under the stricter checks.
@hieblmi hieblmi force-pushed the harden-musig2-handling branch from d61f207 to cc0392a Compare May 29, 2026 09:53
@hieblmi hieblmi merged commit e0e1da5 into lightninglabs:master May 29, 2026
10 checks passed
@hieblmi hieblmi deleted the harden-musig2-handling branch May 29, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants