Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invoice Acceptor: ensure asset ID match between RFQ and HTLC #1299

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

GeorgeTsagk
Copy link
Member

Description

This PR adds an extra strictness check which requires the asset ID of the RFQ quote and HTLC records to match. This is done as an extra strict check to guard against HTLC and RFQ asset ID mismatch, which can lead to malicious behavior where a quote for a different asset is being accounted for when accepting an asset HTLC.

Closes #1255

@GeorgeTsagk GeorgeTsagk self-assigned this Jan 14, 2025
Copy link
Contributor

@ffranr ffranr left a comment

Choose a reason for hiding this comment

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

Good idea!


assetID := s.assetIDFromQuote(rfqID)
for _, v := range htlc.Balances() {
if v.AssetID.Val != *assetID {
Copy link
Contributor

Choose a reason for hiding this comment

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

To avoid dereference of nil at *assetID I think assetIDFromQuote should return asset.ID, error (error in case of missing policy for HTLC).

Copy link
Member

Choose a reason for hiding this comment

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

This is unaddressed. By returning the identifier directly, we can do if !ident.HasId() {.

@dstadulis
Copy link
Collaborator

dstadulis commented Jan 14, 2025

TODO

  • itest unit test to cover the negative cases

@dstadulis dstadulis added this to the v0.6 milestone Jan 14, 2025
@GeorgeTsagk GeorgeTsagk force-pushed the inv-accept-asset-id-match branch 2 times, most recently from ef54607 to 5e76383 Compare January 21, 2025 11:16
@coveralls
Copy link

coveralls commented Jan 21, 2025

Pull Request Test Coverage Report for Build 12906816979

Details

  • 40 of 47 (85.11%) changed or added relevant lines in 1 file are covered.
  • 19 unchanged lines in 6 files lost coverage.
  • Overall coverage increased (+0.07%) to 40.809%

Changes Missing Coverage Covered Lines Changed/Added Lines %
tapchannel/aux_invoice_manager.go 40 47 85.11%
Files with Coverage Reduction New Missed Lines %
tappsbt/create.go 2 53.22%
tapdb/addrs.go 2 75.12%
commitment/tap.go 2 84.32%
tapchannel/aux_leaf_signer.go 3 43.43%
tapgarden/caretaker.go 4 68.5%
tapchannel/aux_invoice_manager.go 6 83.61%
Totals Coverage Status
Change from base Build 12906692426: 0.07%
Covered Lines: 26706
Relevant Lines: 65442

💛 - Coveralls

@GeorgeTsagk GeorgeTsagk force-pushed the inv-accept-asset-id-match branch from 5e76383 to 5b235ef Compare January 21, 2025 11:55
@@ -422,6 +431,42 @@ func TestAuxInvoiceManager(t *testing.T) {
},
},
},
{
name: "asset invoice, wrong asset htlc",
Copy link
Member Author

Choose a reason for hiding this comment

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

Added coverage for this in our unit test cases

@GeorgeTsagk GeorgeTsagk requested review from ffranr and guggero January 21, 2025 11:56
Copy link
Member

@guggero guggero left a comment

Choose a reason for hiding this comment

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

Careful self review of the commits would've prevented 50-60% of my comments...
Approach looks okay to me but can be perhaps made a bit more generic.

tapchannel/aux_invoice_manager.go Outdated Show resolved Hide resolved
tapchannel/aux_invoice_manager_test.go Outdated Show resolved Hide resolved

assetID := s.assetIDFromQuote(rfqID)
for _, v := range htlc.Balances() {
if v.AssetID.Val != *assetID {
Copy link
Member

Choose a reason for hiding this comment

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

This is unaddressed. By returning the identifier directly, we can do if !ident.HasId() {.

tapchannel/aux_invoice_manager_test.go Outdated Show resolved Hide resolved
tapchannel/aux_invoice_manager.go Outdated Show resolved Hide resolved
tapchannel/aux_invoice_manager.go Show resolved Hide resolved
We add a simple identifierFromQuote function that fetches the asset
specifier associated with a certain accepted quote identified by its
rfq ID. We will later use this in the handleInvoice function to
compare the identifier of the quote with that of the HTLCs.
@GeorgeTsagk GeorgeTsagk force-pushed the inv-accept-asset-id-match branch from 5b235ef to 6066385 Compare January 21, 2025 18:02
@GeorgeTsagk GeorgeTsagk requested a review from guggero January 21, 2025 18:05
Copy link
Contributor

@ffranr ffranr left a comment

Choose a reason for hiding this comment

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

Nice! This is pretty much there IMO.

I'm just going to suggest a potential simplification in this review.

tapchannel/aux_invoice_manager.go Outdated Show resolved Hide resolved
In this commit we utilize the previously defined helper in order to
define a new htlc validation function. The validation currently includes
checking that the asset ID of the rfq and the HTLCs always match. This
is done as an extra strict check to guard against HTLC and RFQ asset
specifier mismatch, which can lead to malicious behavior where a quote
for a different asset is being accounted for when accepting an asset
HTLC.
For test cases where an RFQ does not exist we fail earlier, so we need
to update the expected behavior on the tests. We also need to be more
strict when creating dummy RFQs, as now the request with the asset
specifier needs to be present, for the new checks to take place.
@GeorgeTsagk GeorgeTsagk force-pushed the inv-accept-asset-id-match branch from 6066385 to 845e0f6 Compare January 22, 2025 11:04
@GeorgeTsagk GeorgeTsagk requested a review from ffranr January 22, 2025 11:04
Copy link
Member

@guggero guggero left a comment

Choose a reason for hiding this comment

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

Looks good. One more suggestion, but non-blocking.

Comment on lines +394 to +396
if !identifier.HasId() {
return fmt.Errorf("asset specifier has empty assetID")
}
Copy link
Member

Choose a reason for hiding this comment

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

This can be moved to before the loop.

Comment on lines +398 to +401
if v.AssetID.Val != *identifier.UnwrapIdToPtr() {
return fmt.Errorf("mismatch between htlc asset ID " +
"and rfq asset ID")
}
Copy link
Member

Choose a reason for hiding this comment

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

This feels very much against the whole Option paradigm... We could add an ID() or IDOption() method to the specifier that returns the underlying fn.Option directly, then we could do this the "intended" way:

		err := fn.MapOptionZ(
			identifier.IdOption(), func(id asset.ID) error {
				if v.AssetID.Val != id {
					return fmt.Errorf("mismatch between " +
						"htlc asset ID and rfq asset " +
						"ID")
				}

				return nil
			},
		)
		if err != nil {
			return err
		}

But non-blocking.

@Roasbeef Roasbeef modified the milestones: v0.6, v0.5.1 Jan 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 👀 In review
Development

Successfully merging this pull request may close these issues.

Assert invoice-RFQ asset ID and HTLC asset ID match
6 participants