On occasion I use a VivoPay 3500 for reading RFID enabled cards, usually for demos. Well, recently, the VivoPay went missing and I was looking for something to read the data from a Chase Visa PayPass/Blink card. There is ChAP.py from RFIDIOt
(http://rfidiot.org/) but it stumbled reading the Chase card. So I took ChAP.py and slimmed it down to work specifically with the Chase card. The one think I noticed is that i cant seem to identify the Expiration date.
Nonetheless here it is, at the moment it can pull name and card number (thanks goes to samy from the proxmark forums for the query command).
Usage:
./ChasePayPassBlink.py -h
ChasePayPassBlink.py
usage:
ChasePayPassBlink.py [options]
By Brad A. - Completely stripped down version of ChAP.py
from rfidiot.org
Options:
-d Debug - Show PC/SC APDU data
-h Print detailed help message
-r Raw output - do not interpret EMV data
-v Verbose on
-u UnHide CC info
-m Minimal Info (just CC)
Source:
root@bt:/work# cat ChasePayPassBlink.py
#! /usr/bin/env python
"""
This is a modified ChAP.py from RFIDIOt
-Brad Antoniewicz
Script that tries to select the EMV Payment Systems Directory on all inserted cards.
Original Copyright 2008 RFIDIOt
Author: Adam Laurie, mailto:adam@algroup.co.uk
http://rfidiot.org/ChAP.py
"""
from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest
from smartcard.CardConnection import CardConnection
from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver
from smartcard.Exceptions import CardRequestTimeoutException
from smartcard.Exceptions import CardConnectionException
import getopt
import sys
import string
import binascii
from operator import *
import time
# default global options
TryCommand= False
Debug= False
Protocol= CardConnection.T0_protocol
Verbose= False
HideCC= True
MinInfo= False
AID = [0xa0,0x00,0x00,0x00,0x03]
# define the apdus used in this script
GET_RESPONSE = [0x00, 0xC0, 0x00, 0x00 ]
SELECT = [0x00, 0xA4, 0x04, 0x00]
CMD = [0x00, 0xB2, 0x01, 0x0C, 0x00]
# define SW1 return values
SW1_RESPONSE_BYTES= 0x61
SW1_WRONG_LENGTH= 0x6c
SW12_OK= [0x90,0x00]
SW12_NOT_SUPORTED= [0x6a,0x81]
SW12_NOT_FOUND= [0x6a,0x82]
SW12_COND_NOT_SAT= [0x69,0x85] # conditions of use not satisfied
def printhelp():
print '\nChasePayPassBlink.py'
print 'usage:\n\n ChasePayPassBlink.py [options]'
print
print 'By Brad A. - Completely stripped down version of ChAP.py'
print ' from rfidiot.org'
print '\nOptions:\n'
print '\t-d\t\tDebug - Show PC/SC APDU data'
print '\t-h\t\tPrint detailed help message'
print '\t-r\t\tRaw output - do not interpret EMV data'
print '\t-v\t\tVerbose on'
print '\t-u\t\tUnHide CC info'
print '\t-m\t\tMinimal Info (just CC)'
print
def hexprint(data):
index= 0
while index < len(data):
print '%02x' % data[index],
index += 1
print
def textprint(data):
index= 0
out= ''
while index < len(data):
if data[index] >= 0x20 and data[index] < 0x7f:
out += chr(data[index])
else:
out += '.'
index += 1
print out
def try_cmd(cardservice):
le= 0x00
apdu = CMD
response, sw1, sw2 = send_apdu(cardservice,apdu)
if response:
if Verbose:
print '\t[VERBOSE] Got Response!'
return response
else:
print '\t[ERROR] No Response'
return False, 0, ''
def parse_ccdata(response2):
OFFSET_HDR= 3
OFFSET_CC= 4
OFFSET_NAMEFIELD= 23
OFFSET_NAMELEN= 26
print "Response:"
if not MinInfo:
print "\tHeader: ",
index=0
while index <= len(response2[:OFFSET_HDR]):
print '%02x' % response2[index],
print "(%d)" % response2[index],
index += 1
print
print "\tCard Number: ",
index=0
ccnum=""
count=0
while index < len(response2[OFFSET_CC:OFFSET_CC + 8]):
if HideCC:
if count<6:
ccnum += '**'
else:
ccnum += '%02x' % response2[OFFSET_CC + index]
else:
ccnum += '%02x' % response2[OFFSET_CC + index]
index +=1
count +=1
print ccnum
if not MinInfo:
print "\tStuff: ",
index=0
while index < len(response2[OFFSET_CC + 8:OFFSET_NAMEFIELD]):
print '%02x' % response2[OFFSET_CC + 8 + index],
print '(%d)' % response2[OFFSET_CC + 8 + index],
index +=1
print
if response2[OFFSET_NAMEFIELD] == 0x5f and response2[OFFSET_NAMEFIELD + 1] == 0x20:
if Verbose:
print "\tName Field Code Found!: %02x" % response2[OFFSET_NAMEFIELD],
print "%02x" % response2[OFFSET_NAMEFIELD + 1]
else:
print "\t[WARNING]Could not find Name Field Code!! Something might not be good"
length = '%d' % response2[OFFSET_NAMEFIELD + 2]
if Verbose:
print "\t[VERBOSE] Length:",length,"(",int(length),") %02x" % response2[OFFSET_NAMEFIELD + 2]
print "\tName:",
textprint(response2[OFFSET_NAMEFIELD+3:OFFSET_NAMEFIELD+3+int(length)])
if Verbose:
print "\t[VERBOSE] Name (Hex):",
index=0
while index < len(response2[OFFSET_NAMEFIELD+3:OFFSET_NAMEFIELD+3+int(length)]):
print '%02x' % response2[OFFSET_NAMEFIELD + 3 + index],
print '(%s)' % chr(response2[OFFSET_NAMEFIELD + 3 + index]),
index +=1
print
if not MinInfo:
print "\tThe Rest: ",
index=0
while index < len(response2[OFFSET_NAMEFIELD + 3 + int(length):]):
print '%02x' % response2[OFFSET_NAMEFIELD + 3 + int(length) + index],
print '(%d)' % response2[OFFSET_NAMEFIELD + 3 + int(length) + index],
index += 1
print
def check_return(sw1,sw2):
if [sw1,sw2] == SW12_OK:
return True
return False
def send_apdu(cardservice,apdu):
# send apdu and get additional data if required
response, sw1, sw2 = cardservice.connection.transmit( apdu, Protocol )
if sw1 == SW1_WRONG_LENGTH:
# command used wrong length. retry with correct length.
apdu= apdu[:len(apdu) - 1] + [sw2]
return send_apdu(apdu)
if sw1 == SW1_RESPONSE_BYTES:
# response bytes available.
apdu = GET_RESPONSE + [sw2]
response, sw1, sw2 = cardservice.connection.transmit( apdu, Protocol )
return response, sw1, sw2
def select_aid(cardservice,aid):
# select an AID and return True/False plus additional data
apdu = SELECT + [len(aid)] + aid + [0x00]
#apdu = SELECT
response, sw1, sw2= send_apdu(cardservice,apdu)
if check_return(sw1,sw2):
return True, response, sw1, sw2
else:
return False, [], sw1,sw2
def waitforcard():
while(1):
# request any card type
cardtype = AnyCardType()
# request card insertion
print 'Waiting for a Card to enter the reader\'s field...'
# cardrequest = CardRequest( timeout=10, cardType=cardtype )
cardrequest = CardRequest(timeout=None, cardType=cardtype )
cardservice = cardrequest.waitforcard()
# attach the console tracer
if Debug:
observer=ConsoleCardConnectionObserver()
cardservice.connection.addObserver( observer )
# connect to the card
cardservice.connection.connect(Protocol)
print 'Connecting with AID: ',
hexprint(AID)
selected, response, sw1, sw2= select_aid(cardservice,AID)
if selected:
print "\tSuccess!"
print "Response: \n\t",
textprint(response)
if Verbose:
print "\t[VERBOSE]: ",
hexprint(response)
if not MinInfo:
print '\nRequesting Track Info: \n\t',
hexprint(CMD)
response2 = try_cmd(cardservice)
if Verbose:
print "\t[VERBOSE]: ",
hexprint(response2)
print "\t[VERBOSE]: ",
textprint(response2)
parse_ccdata(response2)
# main loop
try:
# 'args' will be set to remaining arguments (if any)
opts, args = getopt.getopt(sys.argv[1:],'dtvum')
for o, a in opts:
if o == '-d':
Debug= True
if o == '-t':
Protocol= CardConnection.T1_protocol
if o == '-v':
Verbose= True
if o == '-u':
HideCC= False
if o == '-m':
MinInfo= True
except getopt.GetoptError:
# -h will cause an exception as it doesn't exist!
printhelp()
sys.exit(True)
try:
waitforcard()
except CardConnectionException:
print 'Wait what happened? Did you remove the card from the field?'
time.sleep(2)
waitforcard()
except KeyboardInterrupt:
print 'Quiting'