Skip to content

Instantly share code, notes, and snippets.

@aivarsk
Created September 28, 2025 08:56
Show Gist options
  • Select an option

  • Save aivarsk/4eb5d1756b36989cde2c38ac4b95c050 to your computer and use it in GitHub Desktop.

Select an option

Save aivarsk/4eb5d1756b36989cde2c38ac4b95c050 to your computer and use it in GitHub Desktop.
Talking with contactless cards
# For Debian/Ubuntu:
# Needed for the pyscard
# sudo apt-get install libpcsclite-dev
# Install dependencies. DO NOT install smartcard - it's a different package.
# pip install ber_tlv pyscard
# Use https://paymentcardtools.com/emv-tlv-parser to decode DATA
#
# IMPORTANT! All examples are hard-coded for specific test cards I had.
# List and size of fields for AC generation may differ.
#
from ber_tlv.tlv import *
from binascii import hexlify, unhexlify
from smartcard.ATR import ATR
from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest
from smartcard.util import toHexString
def transmit(command):
print("CLA: ", hex(command[0]))
print("INS: ", hex(command[1]))
print("P1: ", hex(command[2]))
print("P2: ", hex(command[3]))
print("Lc: ", hex(command[4]))
print("DATA: ", hexlify(bytes(command[5:])).decode().upper())
print("")
data, sw1, sw2 = cardservice.connection.transmit(command)
print("SW1: ", hex(sw1))
print("SW2: ", hex(sw2))
print("DATA: ", toHexString(data).replace(" ", ""))
print("")
#
# This is VISA flow
#
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=5, cardType=cardtype)
cardservice = cardrequest.waitforcard()
cardservice.connection.connect()
atr = ATR(cardservice.connection.getATR())
print(atr)
print('historical bytes: ', toHexString(atr.getHistoricalBytes()))
print('checksum: ', "0x%X" % atr.getChecksum())
print('checksum OK: ', atr.checksumOK)
print('T0 supported: ', atr.isT0Supported())
print('T1 supported: ', atr.isT1Supported())
print('T15 supported: ', atr.isT15Supported())
print("")
print("SELECT PSE")
fname = "2PAY.SYS.DDF01".encode()
transmit([
0x00, 0xA4,
0x04, 0x00,
len(fname)] + list(fname))
print("SELECT AID")
aid = unhexlify("A0000000031010")
transmit([0x00, 0xA4, 0x04, 0x00, len(aid)] + list(aid))
print("GET PROCESSING OPTIONS / GPO")
options = bytes([
# 9F66 04
0x7e, 0xc0, 0x0, 0x0,
# 9F02 06
0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
# 9F03 06
0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
# 9F1A 02
0x04, 0x28,
# 95 05
0x00, 0x00, 0x00, 0x00, 0x00,
# 9A 03
0x23, 0x10, 0x15,
# 5F2A 02
0x09, 0x78,
# 9C 01
0x00,
# 9F37 04
0x42, 0x42, 0x42, 0x42
])
data = Tlv.build([(0x83, options)])
transmit([0x80, 0xA8, 0x00, 0x00, len(data)] + list(data))
#
# This is MasterCard/EMV flow
#
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=5, cardType=cardtype)
cardservice = cardrequest.waitforcard()
cardservice.connection.connect()
atr = ATR(cardservice.connection.getATR())
print(atr)
print('historical bytes: ', toHexString(atr.getHistoricalBytes()))
print('checksum: ', "0x%X" % atr.getChecksum())
print('checksum OK: ', atr.checksumOK)
print('T0 supported: ', atr.isT0Supported())
print('T1 supported: ', atr.isT1Supported())
print('T15 supported: ', atr.isT15Supported())
print("")
print("SELECT PSE")
fname = "2PAY.SYS.DDF01".encode()
transmit([
0x00, 0xA4,
0x04, 0x00,
len(fname)] + list(fname))
print("SELECT AID")
aid = unhexlify("A0000000041010")
transmit([0x00, 0xA4, 0x04, 0x00, len(aid)] + list(aid))
print("GET PROCESSING OPTIONS / GPO")
data = Tlv.build([(0x83, b"")])
transmit([0x80, 0xA8, 0x00, 0x00, len(data)] + list(data))
print("READ RECORD 2 - 1")
# SFI + "P1 is a record number"
transmit([0x00, 0xB2, 0x01, (0x10 & 0xf8) + 0x04, 0x00])
print("READ RECORD 4 - 1")
transmit([0x00, 0xB2, 0x01, (0x20 & 0xf8) + 0x04, 0x00])
print("READ RECORD 4 - 2")
transmit([0x00, 0xB2, 0x02, (0x20 & 0xf8) + 0x04, 0x00])
print("READ RECORD 4 - 3")
transmit([0x00, 0xB2, 0x03, (0x20 & 0xf8) + 0x04, 0x00])
print("READ RECORD 4 - 4")
transmit([0x00, 0xB2, 0x04, (0x20 & 0xf8) + 0x04, 0x00])
print("GENERATE AC")
data = [0x00] * 66
transmit([0x80, 0xAE, 0x80, 0x00, len(data)] + data)
#
# This was MasterCard card emulation on a phone (HCE)
# Implicit select does not work!
#
cardtype = AnyCardType()
cardrequest = CardRequest(timeout=5, cardType=cardtype)
cardservice = cardrequest.waitforcard()
cardservice.connection.connect()
atr = ATR(cardservice.connection.getATR())
print("SELECT AID")
aid = unhexlify("A0000000041010")
transmit([0x00, 0xA4, 0x04, 0x00, len(aid)] + list(aid) + [0x0])
print("GET PROCESSING OPTIONS / GPO")
data = Tlv.build([(0x83, b"\x00" * 11)])
transmit([0x80, 0xA8, 0x00, 0x00, len(data)] + list(data))
print("READ RECORD 2 - 1")
# SFI + "P1 is a record number"
transmit([0x00, 0xB2, 0x01, (0x10 & 0xf8) + 0x04, 0x00])
print("READ RECORD 4 - 1")
transmit([0x00, 0xB2, 0x01, (0x20 & 0xf8) + 0x04, 0x00])
print("READ RECORD 4 - 2")
transmit([0x00, 0xB2, 0x02, (0x20 & 0xf8) + 0x04, 0x00])
print("READ RECORD 4 - 3")
transmit([0x00, 0xB2, 0x03, (0x20 & 0xf8) + 0x04, 0x00])
print("READ RECORD 4 - 4")
transmit([0x00, 0xB2, 0x04, (0x20 & 0xf8) + 0x04, 0x00])
print("GENERATE AC")
data = [0x00] * 66
transmit([0x80, 0xAE, 0x80, 0x00, len(data)] + data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment