Reading data from credit cards and Polish Electronic Student Cards (ELS)
Jacek Kowalski
2015-03-08 69d2540d3d3882eb742dfaa99ab055b2e57b0bc9
Support for Electronic Student Card
1 files modified
166 ■■■■■ changed files
sc.py 166 ●●●●● patch | view | raw | blame | history
sc.py
@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import smartcard.CardRequest
import smartcard.CardConnectionObserver
import smartcard.ExclusiveTransmitCardConnection
import smartcard.pcsc.PCSCPart10
from pyasn1.type import univ
from pyasn1.codec.ber import decoder
import subprocess
from smartcard.util import toHexString, toASCIIBytes, toBytes, toASCIIString
@@ -17,7 +19,7 @@
    report = 0
    def __init__(self, level, message):
        if level >= SCardDebug.report:
            print(message)
            print message
class SCardConnectionObserver(smartcard.CardConnectionObserver.CardConnectionObserver):
    def update( self, cardconnection, ccevent ):
@@ -66,14 +68,7 @@
    MSB = 0x80
    CONSTRUCTED = 0x20
    
    debug = True
    tags = {
        0x02: ["Integer", ""],
        0x03: ["Bit string", ""],
        0x04: ["Octet string", ""],
        0x05: ["Null", ""],
        0x06: ["Object identifier", ""],
        0x42: ["Issuer Identification Number (IIN)", "The number that identifies the major industry and the card issuer and that forms the first part of the Primary Account Number (PAN)"],
        0x4F: ["Application Identifier (AID) - card", "Identifies the application as described in ISO/IEC 7816-5"],
        0x50: ["Application Label", "Mnemonic associated with the AID according to ISO/IEC 7816-5"],
@@ -215,8 +210,6 @@
        if tag == 0x9f8004:
            tag = 0x9f80
            data.append(0x04)
        if SCardTLV.debug:
            print('Got tag: ' + toHexString(tag))
        
        length = data.pop()
        if length == 0x80:
@@ -251,27 +244,24 @@
            if constructed:
                read = SCardTLV.decode(read)
            
            if tag in ret:
                ret[tag].push(read)
            else:
                ret[tag] = [ read ]
            ret[tag] = read
        
        return ret
    
    @staticmethod
    def displayTag(data, intend=""):
        if data[0] in SCardTLV.tags:
            print(intend + SCardTLV.tags[data[0]][0] + " [" + hex(data[0]) + "], length "+str(data[1]))
            print intend + SCardTLV.tags[data[0]][0] + " [" + hex(data[0]) + "], length "+str(data[1])
        else:
            print(intend + "UNKNOWN TAG [" + hex(data[0]) + "], length "+str(data[1]))
            print intend + "UNKNOWN TAG [" + hex(data[0]) + "], length "+str(data[1])
    
    @staticmethod
    def display(data, intend="", singleline = None):
        for tag, value in data.items():
            if tag in SCardTLV.tags:
                print(intend + SCardTLV.tags[tag][0] + " [" + hex(tag) + "]:")
                print intend + SCardTLV.tags[tag][0] + " [" + hex(tag) + "]:"
            else:
                print(intend + "UNKNOWN TAG [" + hex(tag) + "]:")
                print intend + "UNKNOWN TAG [" + hex(tag) + "]:"
            
            if tag in SCardTLV.specialTags:
                value.reverse()
@@ -280,7 +270,7 @@
            elif isinstance(value, dict):
                SCardTLV.display(value, intend+"\t")
            else:
                print(intend+"\t"+toHexString(value)+" ("+toASCIIString(value)+")")
                print intend+"\t"+toHexString(value)+" ("+toASCIIString(value)+")"
class SCardSelectFileBy:
    MFDFEF = 0x00
@@ -330,7 +320,7 @@
        self.connection = conn
    
    def getFeatures(self):
        print(smartcard.pcsc.PCSCPart10.getFeatureRequest(self.connection))
        print smartcard.pcsc.PCSCPart10.getFeatureRequest(self.connection)
class SCardEMVPINEncode:
    @staticmethod
@@ -391,7 +381,7 @@
        exclusive = smartcard.ExclusiveTransmitCardConnection.ExclusiveTransmitCardConnection(serv.connection)
        exclusive.lock()
        time.sleep(0.2)
        print("inserted", toHexString(serv.connection.getATR()))
        print "inserted", toHexString(serv.connection.getATR())
        
        self.service = serv
        self.connection = serv.connection
@@ -404,6 +394,7 @@
    
    def _sendAPDU(self, adpu = []):
        response, SW1, SW2 = self.exclusive.transmit(adpu)
        if SW1 != 0x90:
            raise SCardResponseException(SW1, SW2)
        
@@ -428,6 +419,9 @@
        
        return self._sendAPDU(adpu)
    
    def GetResponse(self, CLA = 0x00, P1 = 0x00, P2 = 0x00, Le = 0x00):
        return self._sendAPDU([CLA, 0xC0, P1, P2, Le])
    def ReadBinary(self, CLA = 0x00, offset = 0x00, Le = 0x00):
        try:
            return self._sendAPDU([CLA, 0xB0, (offset >> 8) & 0x7F, offset & 0xFF, Le])
@@ -436,18 +430,6 @@
                return self.ReadBinary(CLA, offset, Le = e.SW2)
            else:
                raise e
    def ReadBinaryAll(self, CLA = 0x00):
        data = []
        while len(data) <= 0x7FFF:
            try:
                response, SW1, SW2 = self.ReadBinary(CLA, len(data))
                data += response
            except SCardResponseException as e:
                if e.SW1 == 0x6B:
                    break
                raise e
        return data
    
    def ReadRecord(self, CLA = 0x00, record = 0x00, identifier = 0x00, Le = 0x00):
        try:
@@ -485,12 +467,45 @@
        
        return self._sendAPDU(adpu)
class SCardActions:
    def __init__(self, card):
        self.card = card
    def cdRoot(self, CLA = 0x00):
        return self.cd("3F20")
    def cdAID(self, AID, CLA = 0x00):
        return self.card.SelectFile(CLA = CLA, by = SCardSelectFileBy.DFname, data = toBytes(AID), Le = 0)
    def cd(self, MFEFDF, CLA = 0x00):
        return self.card.SelectFile(CLA = CLA, by = SCardSelectFileBy.MFDFEF, data = toBytes(MFEFDF), Le = 0)
    def readBinaryEF(self, EF, blockSize = 4, CLA = 0x00):
        attrs = self.cd(EF, CLA = CLA)
        data = []
        offset = 0
        try:
            while True:
                offset = int(len(data) / blockSize)
                result, _, _ = self.card.ReadBinary(offset = offset, CLA = CLA)
                if result == []:
                    break
                data = data[0:offset * blockSize] + result
        except SCardResponseException as e:
            if e.SW1 == 0x6B:
                pass
            else:
                raise e
        return data
class SCardHandler:
    @staticmethod
    def getCard():
        request = smartcard.CardRequest.CardRequest(newcardonly=True, timeout=None)
        print("Insert card...")
        print "Insert card..."
        service = request.waitforcard()
        print "Card detected..."
        time.sleep(0.5)
        return SCard(service)
@@ -500,25 +515,24 @@
    ]
    
    def __init__(self, card):
        self.card = card
        self.card = SCardActions(card)
    
    def selectAID(self):
        #self.card.SelectFile(by = SCardSelectFileBy.MFDFEF, data = toBytes("3F20"))
        for aid in SCardELS.aids:
            try:
                self.card.SelectFile(by = SCardSelectFileBy.DFname, data = toBytes(aid), Le = 0)
                return True
                return self.card.cdAID(aid)
            except:
                pass
                raise Exception('ELS AID not available')
        return False
    
    def readCert(self):
        self.card.SelectFile(by = SCardSelectFileBy.childEF, data = toBytes("0001"), Le = 0)
        return self.card.ReadBinaryAll()
        data = self.card.readBinaryEF("0001")
        return data
    
    def readData(self):
        self.card.SelectFile(by = SCardSelectFileBy.childEF, data = toBytes("0002"), Le = 0)
        return self.card.ReadBinaryAll()
        data = self.card.readBinaryEF("0002")
        return data
class SCardEMV:
    aids = [
@@ -567,7 +581,7 @@
                    data, SW1, SW2 = self.card.ReadRecord(record = i, identifier = (sfi << 3) | SCardReadRecordIdentifier.P1isRecordNumber | SCardReadRecordIdentifier.ReadP1)
                    records.append(SCardTLV.decode(data))
            except SCardResponseException as e:
                if e.SW1 != 0x6A or e.SW2 != 0x83:
                if e.SW1 != 0x6A or (e.SW2 != 0x83 and e.SW2 != 0x88):
                    raise SCardResponseException(e.SW1, e.SW2)
            
            return self.card.SelectFile(by = SCardSelectFileBy.DFname, data = records[0][0x70][0x61][0x4f], Le = 0x00)
@@ -647,7 +661,7 @@
                data, SW1, SW2 = self.card.ReadRecord(record = i, identifier = (sfi << 3) | SCardReadRecordIdentifier.P1isRecordNumber | SCardReadRecordIdentifier.ReadP1)
                SCardTLV.display(SCardTLV.decode(data))
        except SCardResponseException as e:
            if e.SW1 != 0x6A or e.SW2 != 0x83:
            if e.SW1 != 0x6A or (e.SW2 != 0x83 and e.SW2 != 0x88):
                raise SCardResponseException(e.SW1, e.SW2)
    
    def readData(self):
@@ -662,14 +676,18 @@
            first = tag >> 8
            second = tag & 255
            
            try:
            data, SW1, SW2 = self.card.GetData(CLA = 0x80, P1 = first, P2 = second)
            SCardTLV.display(SCardTLV.decode(data))
            except SCardResponseException as e:
                if e.SW1 != 0x6A or (e.SW2 != 0x83 and e.SW2 != 0x88):
                    raise SCardResponseException(e.SW1, e.SW2)
    
    def PIN(self, data = ""):
        data, SW1, SW2 = self.card.Verify(codeType = SCardVerifyCodeTypeEMV.plaintext, data = SCardEMVPINEncode.plaintext(data))
ELS = 1
ELS = 0
if ELS:
    try:
@@ -678,8 +696,56 @@
        els = SCardELS(card)
        
        els.selectAID()
        SCardTLV.decode(els.readCert())
        SCardTLV.decode(els.readData())
        cert = toASCIIString(els.readCert())
        openssl = subprocess.Popen(['openssl', 'x509', '-text', '-noout', '-inform', 'DER'],
                stdin = subprocess.PIPE, stdout = subprocess.PIPE)
        stdout, stderr = openssl.communicate(cert)
        print ""
        print stdout
        print ""
        data = decoder.decode(toASCIIString(els.readData()))[0]
        data_signatureType = data.getComponentByPosition(0)
        data_signature = data.getComponentByPosition(1)
        assert(data_signatureType.asTuple() == univ.ObjectIdentifier('1.2.840.113549.1.7.2').asTuple())
        data_signature_version = data_signature.getComponentByPosition(0)
        assert(data_signature_version >= 3)
        data_signature_algorithms = data_signature.getComponentByPosition(1)
        data_signature_data = data_signature.getComponentByPosition(2)
        data_signature_info = data_signature.getComponentByPosition(data_signature_version)
        assert(data_signature_info != None)
        data_signature_data_type = data_signature_data.getComponentByPosition(0)
        data_signature_data_data = data_signature_data.getComponentByPosition(1)
        assert(data_signature_data_type.asTuple() == univ.ObjectIdentifier('1.2.616.1.101.4.1.1.1').asTuple())
        els = data_signature_data_data.asOctets()
        els = decoder.decode(els)[0]
        els_version = els.getComponentByPosition(0)
        assert(els_version == 1)
        els_serialNumber = els.getComponentByPosition(1)
        els_university = els.getComponentByPosition(2)
        els_surnames = els.getComponentByPosition(3)
        els_surnames_tuple = [str(els_surnames.getComponentByPosition(i)) for i in range(len(els_surnames))]
        els_surnames_printable = ' '.join(els_surnames_tuple)
        els_names = els.getComponentByPosition(4)
        els_names_tuple = [str(els_names.getComponentByPosition(i)) for i in range(len(els_names))]
        els_names_printable = ' '.join(els_names_tuple)
        els_studentNumber = els.getComponentByPosition(5)
        els_editId = els.getComponentByPosition(6)
        els_personalNumber = els.getComponentByPosition(7)
        els_expiry = els.getComponentByPosition(8)
        print "Uczelnia: " + els_university
        print "Nazwisko: " + els_surnames_printable
        print "Imię:     " + els_names_printable
        print "Album:    " + els_studentNumber
        print "PESEL:    " + els_personalNumber
        print "Ważność:  " + els_expiry
        print "Edycja:   " + els_editId
    except (KeyboardInterrupt, SystemExit):
        pass
    except: