Introduction
Here we describe the steps for making your own, self-sovereign, self-custodial, contactless payment card to pay with Bitcoin over the Lightning Network.
The card uses open standards throughout and contains mitigation for replay attacks.
With this setup you can use your own server to authenticate a card tap, apply custom checks as required and make payment.
Contactless payment cards use Near Field Communication (NFC) with NFC Data Exchange Format (NDEF) messaging. Normally this would transmit a static message containing a link or other data but this would not be suitable for a payment card as the card could easily be copied onto another card. The interesting thing for us about the NTAG 424 DNA card (and others) by NXP is the Secure Unique NFC (SUN) message feature. SUN enables the card to be set up with secret keys to produce a message which changes each time it is read in a predictable way that can be verified on your server before it sends the payment.

Secure Unique NFC (SUN)
Ok - let’s make one.
Resources
-
some cards
- either blank NXP DNA 424 NTAG cards/keyfobs/stickers
- or CoinCorner Bolt cards
-
a good NFC reader/writer
- Identiv uTrust 3700 F
-
software
-
reference documents
Steps
Check our setup
- start the NFC TagXplorer software
Connectto the NFC card reader- place a card on the reader and click
Connect Tag - verify the card description
Read the card
- select
NDEF OperationsandRead NDEF - if you get this error, click
Format NDEFand try again
- verify that the read completes without erroring
Start to set up the URI
- select
NTAG Operations,Mirroring FeaturesandNTAG 424 DNA - set
Protocoltohttps:// - set
URI Datato
lnurlw://card.yourdomain.com
- select
Add PICCDATAandEnable SUN Message - adjust the
URI Datato
lnurlw://card.yourdomain.com?p=00000000000000000000000000000000&c=0000000000000000
- click after
p=and note the p_position (38 in this case) - click after
c=and note the c_position (73 in this case) - select
Write To Tag
- now go back to
NDEF OperationsandRead NDEF - verify that the
NDEF Payload Infois as expected
Finish setting up the URI template
- notice that the URI shows as
https://lnurlw://card ...but we wantlnurlw://card ... - go to
NTAG OperationsandNTAG 424 DNA - select
Read/Write data - select
File NoasNDEF File - 02 - click
Read
- the NDEF file is
0057D1015355046C6E75726C .... - look for the bytes
5504(6 bytes from the start) 04is the code forhttps://URI prepending- change the
04to00to indicate no prepending for the URI
- click
Write - now go back to
NDEF OperationsandRead NDEF - verify that the
NDEF Payload(HEX) Infois similar to that shown
- copy the hex data and convert to text, without the
0x00prefix - verify you have your expected
URI datavalue
Online hex to text tool
Set up the SUN authentication message
- go to
NTAG OperationsandNTAG 424 DNA - select
Security Managementand clickAuthentiate First
- select
Get/Change File Settings
- set up the values in the order shown
- select
Change File Settings
- now go back to
NDEF OperationsandRead NDEF - convert the hex data to text again
- verify that the
pandcvalues are non zero - select
Read NDEFagain - convert the hex data to text again
- verify that the
pandcvalues are in the right place - verify that the
pandcvalues change on each read
Change the application keys
- go to
NTAG OperationsandNTAG 424 DNA - select
Security Management - select
Authenticate - leave the
Card Key Noset to00 - leave the
Keyvalue set to00000000000000000000000000000000if not changed yet - click
Authenticate First
- select
Change Key - select the
Card Key Noto change the key value for00to04 - leave the
Old Keyvalue set to00000000000000000000000000000000if not changed yet - enter a
New Keyvalue as required - enter a
New Key Versionvalue of00or as required to keep track of your keys - click
Change Key
- repeat this to change all 5 application keys to your own values
Using the card
- the point-of-sale (POS) system will read the NDEF message from your card
- for example, NDEF message is
lnurlw://card.yourdomain.com?p=A2EF40F6D46F1BB36E6EBF0114D4A464&c=F509EEA788E37E32
- the POS will call your server here
https://card.yourdomain.com?p=A2EF40F6D46F1BB36E6EBF0114D4A464&c=F509EEA788E37E32
- your server should verify the payment request and issue an LNURLw response
LNURLw references
LUD-01: Base LNURL encoding and decoding
LUD-03: withdrawRequest base spec.
LUD-17: Protocol schemes and raw (non bech32-encoded) URLs.
Server side verification
- select
NDEF OperationsandRead NDEF - convert the hex data to text
- for the
pvalue and theSDM Meta Read Access Keyvalue, decrypt the UID and counter - for the
cvalue and theSDM File Read Access Keyvalue, check with AES-CMAC

decrypt and authenticate calculations
- the authenticated UID and counter values can be used on your server to verify the request
- your server should only accept an increasing counter value
- additional validation rules can be added at your server, for example
- an enable flag
- payment limits
- a list of allowed merchants
- a verification of your location from your phone
- your server can then make payment from your lightning node
Conclusion
We have shown that you can create your own, self-sovereign, self-custodial, contactless payment card for using Bitcoin over the Lightning Network.