Skip to content

Conversation

Bicaru20
Copy link

@Bicaru20 Bicaru20 commented Aug 8, 2025

Description

This pull request adds support for verifying DNSSEC proofs in Partially Signed Bitcoin Transactions (PSBTs) within SeedSigner, implementing BIP-353 (DNS-based Human-Readable Names for Bitcoin Wallets). When a user scans a PSBT, SeedSigner now checks for a DNS name (e.g., craig@sparrowwallet.com) and its associated DNSSEC proof. If both are present, SeedSigner verifies the proof against the provided DNS name. Based on the result, one of two new screens will appear:

  • New ScreensSuccess Screen (PSBTDnsNameView):
PSBTDnsNameView
  • Error Screen:
Screenshot from 2025-08-08 22-24-27

Shows an error message if the DNSSEC proof is invalid or not found, then navigates back to the main screen.

Dependencies

This feature relies on a specific version of the embit library, available at diybitcoinhardware/embit#102. This version includes BIP-353 support for offline DNSSEC proof verification. Without it, the feature will not work.

This pull request is categorized as a:

  • New feature
  • Bug fix
  • Code refactor
  • Documentation
  • Other

Checklist

  • I’ve run pytest and made sure all unit tests pass before sumbitting the PR (with the embit version previously mentioned)

If you modified or added functionality/workflow, did you add new unit tests?

  • Not yet
  • Yes
  • N/A

I have tested this PR on the following platforms/os:

TO DO:

  • Add new tests

Copy link
Contributor

@alvroble alvroble left a comment

Choose a reason for hiding this comment

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

Hi @Bicaru20 and thank you for your work! I left some comments but in general I feel that this PR is going on the right direction. Currently it will not pass the test because it depends on the PR to embit but I tested it using Raspberry Pi OS Manual Build env along Sparrow Wallet and it works!

imagen imagen

@@ -204,6 +205,38 @@ def run(self):



class PSBTDnsNameView(View):
Copy link
Contributor

Choose a reason for hiding this comment

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

This name may be too generic. It'd make sense to use something more related to BIP-353. Maybe PSBTHrnView or PSBTDnsPaymentIntructionsView

class PSBTDnsNameView(View):
def run(self):
from seedsigner.gui.screens.screen import LargeIconStatusScreen
from embit.bip353 import verify_dns_proof
Copy link
Contributor

Choose a reason for hiding this comment

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

This line will raise an error until diybitcoinhardware/embit#102 gets merged

title = _("DNS name verification"),
button_data = [ButtonOption("Review math")],
text = _("Verification complete. Review the DNS name."),
status_headline = dns_name,
Copy link
Contributor

Choose a reason for hiding this comment

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

This will print out pay.user._bitcoin-payment.satsto.me for HRN pay@satsto.me.

SHOULD display ₿pay@satsto.me. Currently ₿ displays as an unknown character in SeedSigner, so maybe it makes sense to leave it as pay@satsto.me for now.




class PSBTDnsNameErrorView(View):
Copy link
Contributor

Choose a reason for hiding this comment

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

Same about the naming. As it is an error view, should use a name that identifies the reason of error which is an invalid DNSSEC proof for the provided HRN. Maybe PSBTDnssecProofError or PSBTDnsPaymentIntructionsError

\xaer\x05\x03\x11\t\x8c\xd4\xcf\xaa\x07\x96\x9b\x05craig\x04user\x10_bitcoin-payment\rsparrowwallet\x03com\x00\x00\x10\x00\x01\x00\x00\x0e\x0f\x0032bitcoin:bc1qwthe43xeuasklclq4kvhreluv3hu92rzej42js\x05craig\x04user\x10_bitcoin-payment\rsparrowwallet\x03com\x00\x00.\x00\x01\x00\x00\x0e\x0f\x00e\x00\x10\r\x05\x00\x00\x0e\x10h\x91\xef\xe0h~)`\xbb&\rsparrowwallet \
\x03com\x00\xe7\xae\x93\xd2;tw7ULMR\xdd\x1e\xc0\xf5\x8cA\x1cjGM\xa4l<$\xd0\xdb\x97\r\x86\xe9\x1b\xf9\x1b^\xab\xeb\x1e\xd5\x91!g\x8e\xf54\xa2Zou\xce\x05\x88\xe6RD9\xc1\x1d \x8f0\x1dF')

def PSBTaddwrongDNSSECproof():
Copy link
Contributor

Choose a reason for hiding this comment

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

Invalid DNSSEC proof test vector should use one of the proposed in here that are presumably going to be in the BIP soon.

Copy link
Contributor

Choose a reason for hiding this comment

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

I've never messed around much with PSBTs, but is a good practice to do all this flow testinghere? My guts tell me to just display here both new views and move this flow testing to test_flows_psbt.py + test_psbt_parser.py

Copy link
Author

Choose a reason for hiding this comment

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

I don't know if it is the right place, but my intention was to create the screenshots of the two new screens. I still have to add the tests to test_flows_psbt.py + test_psbt_parser.py

@@ -219,6 +222,9 @@ def _parse_outputs(self):
self.destination_amounts.append(self.psbt.tx.vout[i].value)
self.spend_amount += self.psbt.tx.vout[i].value

if out.dnssec_proof:
Copy link
Contributor

Choose a reason for hiding this comment

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

This line will raise an error in all PSBT parser test cases until diybitcoinhardware/embit#102 gets merged

Copy link
Author

Choose a reason for hiding this comment

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

How should I handle this? Put a try and except?

Copy link
Contributor

Choose a reason for hiding this comment

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

Since this functionality depends on the embit PR, this PR should only be merged into SeedSigner once those changes are merged into embit. So there’s no immediate need to add temporary error handling here.

dns_name = dnssec_data[0].decode('utf-8').replace('@', '.user._bitcoin-payment.')
proof = dnssec_data[1]

verified = verify_dns_proof(dns_name+'.', proof)
Copy link
Contributor

@alvroble alvroble Aug 9, 2025

Choose a reason for hiding this comment

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

More checking should happen here. What if the dns_name is not the same as the verified by the proof? I'm not sure if we should display the hrn from from the PSBT field or from the proof.

Copy link
Author

Choose a reason for hiding this comment

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

If I understood it correctly and as explained in #768 , the dns_name should not be extracted from the proof as it may be different of the dns_name of the PSBT. When validating, we look if the proof allows that name. The way I am doing this is to first look if the verification was successful and then check if the proof allows for that name. I do that in the next line when checking if verified['verified_rrs'] have any records, as that would mean the proof is valid for that name. Do you think my approach is correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I see now, you are right. dns_name should always come from the PSBT, and the proof just gets validated against that value. We don’t extract or compare a name from the proof itself. So my earlier note isn’t needed here. The key check is simply whether the proof validates for the PSBT’s dns_name, which is done entirely by dnssec-prover.

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