Reading data from credit cards and Polish Electronic Student Cards (ELS)
Jacek Kowalski
2015-03-08 69d2540d3d3882eb742dfaa99ab055b2e57b0bc9
commit | author | age
69d254 1 # -*- coding: utf-8 -*-
JK 2 import smartcard.CardRequest
3 import smartcard.CardConnectionObserver
4 import smartcard.ExclusiveTransmitCardConnection
5 import smartcard.pcsc.PCSCPart10
6
7 from pyasn1.type import univ
8 from pyasn1.codec.ber import decoder
9 import subprocess
10
11 from smartcard.util import toHexString, toASCIIBytes, toBytes, toASCIIString
12
13 import collections, sys, traceback, time
14
15 class SCardDebug:
16     DEBUG = 0
17     WARNING = 1
18     ERROR = 2
19     report = 0
20     def __init__(self, level, message):
21         if level >= SCardDebug.report:
22             print message
23
24 class SCardConnectionObserver(smartcard.CardConnectionObserver.CardConnectionObserver):
25     def update( self, cardconnection, ccevent ):
26         if 'connect' == ccevent.type:
27             SCardDebug(SCardDebug.DEBUG, 'connecting to ' + cardconnection.getReader())
28         elif 'disconnect' == ccevent.type:
29             SCardDebug(SCardDebug.DEBUG, 'disconnecting from ' + cardconnection.getReader())
30         elif 'command' == ccevent.type:
31             if isinstance(ccevent.args[0], collections.Sequence):
32                 SCardDebug(SCardDebug.DEBUG, 'send ' + toHexString(ccevent.args[0]))
33         elif 'response' == ccevent.type:
34             if isinstance(ccevent.args[0], collections.Sequence):
35                 SCardDebug(SCardDebug.DEBUG, 'recv ' + toHexString(ccevent.args[0]) + " " + toHexString(ccevent.args[-2:]))
36
37 class SCardInternalException(Exception):
38     SCardEMV_1PAY_Not_Supported = 123514
39     def __init__(self, code, msg):
40         self.code = code
41         self.msg = msg
42     
43     def __str__(self):
44         return "Internal Exception " + self.code + ": " + self.msg
45
46 class SCardResponseException(Exception):
47     errors = {
48         0x6700 : "Wrong Lc",
49         0x6984 : "Referenced data invalidated",
50         0x6A81 : "Function not supported",
51         0x6A82 : "File not found",
52         0x6A86 : "Incorrect parameters P1, P2",
53         0x6A87 : "Lc inconsistent"
54     }
55     def __init__(self, SW1, SW2):
56         self.SW1 = SW1
57         self.SW2 = SW2
58         code = (SW1 << 8) + SW2
59         if code in SCardResponseException.errors:
60             self.msg = SCardResponseException.errors[code]
61         else:
62             self.msg = "Unknown error"
63     def __str__(self):
64         return self.msg
65
66 class SCardTLV:
67     EXTENDED = 0x1F
68     MSB = 0x80
69     CONSTRUCTED = 0x20
70     
71     tags = {
72         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)"],
73         0x4F: ["Application Identifier (AID) - card", "Identifies the application as described in ISO/IEC 7816-5"],
74         0x50: ["Application Label", "Mnemonic associated with the AID according to ISO/IEC 7816-5"],
75         0x57: ["Track 2 Equivalent Data", "Contains the data elements of track 2 according to ISO/IEC 7813, excluding start sentinel, end sentinel, and Longitudinal Redundancy Check (LRC), as follows: Primary Account Number (n, var. up to 19) Field Separator (Hex 'D') (b) Expiration Date (YYMM) (n 4) Service Code (n 3) Discretionary Data (defined by individual payment systems) (n, var.) Pad with one Hex 'F' if needed to ensure whole bytes (b)"],
76         0x5A: ["Application Primary Account Number (PAN)", "Valid cardholder account number"],
77         0x5F20: ["Cardholder Name", "Indicates cardholder name according to ISO 7813"],
78         0x5F24: ["Application Expiration Date", "Date after which application expires"],
79         0x5F25: ["Application Effective Date", "Date from which the application may be used"],
80         0x5F28: ["Issuer Country Code", "Indicates the country of the issuer according to ISO 3166"],
81         0x5F2A: ["Transaction Currency Code", "Indicates the currency code of the transaction according to ISO 4217"],
82         0x5F2D: ["Language Preference", "1-4 languages stored in order of preference, each represented by 2 alphabetical characters according to ISO 639 Note: EMVCo strongly recommends that cards be personalised with data element '5F2D' coded in lowercase, but that terminals accept the data element whether it is coded in upper or lower case."],
83         0x5F30: ["Service Code", "Service code as defined in ISO/IEC 7813 for track 1 and track 2"],
84         0x5F34: ["Application Primary Account Number (PAN) Sequence Number", "Identifies and differentiates cards with the same PAN"],
85         0x5F36: ["Transaction Currency Exponent", "Indicates the implied position of the decimal point from the right of the transaction amount represented according to ISO 4217"],
86         0x5F50: ["Issuer URL", "The URL provides the location of the Issuer's Library Server on the Internet."],
87         0x5F53: ["International Bank Account Number (IBAN)", "Uniquely identifies the account of a customer at a financial institution as defined in ISO 13616."],
88         0x5F54: ["Bank Identifier Code (BIC)", "Uniquely identifies a bank as defined in ISO 9362."],
89         0x5F55: ["Issuer Country Code (alpha2 format)", "Indicates the country of the issuer as defined in ISO 3166 (using a 2 character alphabetic code)"],
90         0x5F56: ["Issuer Country Code (alpha3 format)", "Indicates the country of the issuer as defined in ISO 3166 (using a 3 character alphabetic code)"],
91         0x61: ["Application Template", "Contains one or more data objects relevant to an application directory entry according to ISO/IEC 7816-5"],
92         0x6F: ["File Control Information (FCI) Template", "Identifies the FCI template according to ISO/IEC 7816-4"],
93         0x70: ["EMV Proprietary Template", "Template proprietary to the EMV specification"],
94         0x71: ["Issuer Script Template 1", "Contains proprietary issuer data for transmission to the ICC before the second GENERATE AC command"],
95         0x72: ["Issuer Script Template 2", "Contains proprietary issuer data for transmission to the ICC after the second GENERATE AC command"],
96         0x73: ["Directory Discretionary Template", "Issuer discretionary part of the directory according to ISO/IEC 7816-5"],
97         0x77: ["Response Message Template Format 2", "Contains the data objects (with tags and lengths) returned by the ICC in response to a command"],
98         0x80: ["Response Message Template Format 1", "Contains the data objects (without tags and lengths) returned by the ICC in response to a command"],
99         0x81: ["Amount, Authorised (Binary)", "Authorised amount of the transaction (excluding adjustments)"],
100         0x82: ["Application Interchange Profile", "Indicates the capabilities of the card to support specific functions in the application"],
101         0x83: ["Command Template", "Identifies the data field of a command message"],
102         0x84: ["Dedicated File (DF) Name", "Identifies the name of the DF as described in ISO/IEC 7816-4"],
103         0x86: ["Issuer Script Command", "Contains a command for transmission to the ICC"],
104         0x87: ["Application Priority Indicator", "Indicates the priority of a given application or group of applications in a directory"],
105         0x88: ["Short File Identifier (SFI)", "Identifies the SFI to be used in the commands related to a given AEF or DDF. The SFI data object is a binary field with the three high order bits set to zero."],
106         0x89: ["Authorisation Code", "Value generated by the authorisation authority for an approved transaction"],
107         0x8A: ["Authorisation Response Code", "Code that defines the disposition of a message"],
108         0x8C: ["Card Risk Management Data Object List 1 (CDOL1)", "List of data objects (tag and length) to be passed to the ICC in the first GENERATE AC command"],
109         0x8D: ["Card Risk Management Data Object List 2 (CDOL2)", "List of data objects (tag and length) to be passed to the ICC in the second GENERATE AC command"],
110         0x8E: ["Cardholder Verification Method (CVM) List", "Identifies a method of verification of the cardholder supported by the application"],
111         0x8F: ["Certification Authority Public Key Index", "Identifies the certification authority's public key in conjunction with the RID"],
112         0x90: ["Issuer Public Key Certificate", "Issuer public key certified by a certification authority"],
113         0x91: ["Issuer Authentication Data", "Data sent to the ICC for online issuer authentication"],
114         0x92: ["Issuer Public Key Remainder", "Remaining digits of the Issuer Public Key Modulus"],
115         0x93: ["Signed Static Application Data", "Digital signature on critical application parameters for SDA"],
116         0x94: ["Application File Locator (AFL)", "Indicates the location (SFI, range of records) of the AEFs related to a given application"],
117         0x95: ["Terminal Verification Results", "Status of the different functions as seen from the terminal"],
118         0x97: ["Transaction Certificate Data Object List (TDOL)", "List of data objects (tag and length) to be used by the terminal in generating the TC Hash Value"],
119         0x98: ["Transaction Certificate (TC) Hash Value", "Result of a hash function specified in Book 2, Annex B3.1"],
120         0x99: ["Transaction Personal Identification Number (PIN) Data", "Data entered by the cardholder for the purpose of the PIN verification"],
121         0x9A: ["Transaction Date", "Local date that the transaction was authorised"],
122         0x9B: ["Transaction Status Information", "Indicates the functions performed in a transaction"],
123         0x9C: ["Transaction Type", "Indicates the type of financial transaction, represented by the first two digits of ISO 8583:1987 Processing Code"],
124         0x9D: ["Directory Definition File (DDF) Name", "Identifies the name of a DF associated with a directory"],
125         0x9F01: ["Acquirer Identifier", "Uniquely identifies the acquirer within each payment system"],
126         0x9F02: ["Amount, Authorised (Numeric)", "Authorised amount of the transaction (excluding adjustments)"],
127         0x9F03: ["Amount, Other (Numeric)", "Secondary amount associated with the transaction representing a cashback amount"],
128         0x9F04: ["Amount, Other (Binary)", "Secondary amount associated with the transaction representing a cashback amount"],
129         0x9F05: ["Application Discretionary Data", "Issuer or payment system specified data relating to the application"],
130         0x9F06: ["Application Identifier (AID) - terminal", "Identifies the application as described in ISO/IEC 7816-5"],
131         0x9F07: ["Application Usage Control", "Indicates issuer's specified restrictions on the geographic usage and services allowed for the application"],
132         0x9F08: ["Application Version Number", "Version number assigned by the payment system for the application"],
133         0x9F09: ["Application Version Number", "Version number assigned by the payment system for the application"],
134         0x9F0B: ["Cardholder Name Extended", "Indicates the whole cardholder name when greater than 26 characters using the same coding convention as in ISO 7813"],
135         0x9F0D: ["Issuer Action Code - Default", "Specifies the issuer's conditions that cause a transaction to be rejected if it might have been approved online, but the terminal is unable to process the transaction online"],
136         0x9F0E: ["Issuer Action Code - Denial", "Specifies the issuer's conditions that cause the denial of a transaction without attempt to go online"],
137         0x9F0F: ["Issuer Action Code - Online", "Specifies the issuer's conditions that cause a transaction to be transmitted online"],
138         0x9F10: ["Issuer Application Data", "Contains proprietary application data for transmission to the issuer in an online transaction"],
139         0x9F11: ["Issuer Code Table Index", "Indicates the code table according to ISO/IEC 8859 for displaying the Application Preferred Name"],
140         0x9F12: ["Application Preferred Name", "Preferred mnemonic associated with the AID"],
141         0x9F13: ["Last Online Application Transaction Counter (ATC) Register", "ATC value of the last transaction that went online"],
142         0x9F14: ["Lower Consecutive Offline Limit", "Issuer-specified preference for the maximum number of consecutive offline transactions for this ICC application allowed in a terminal with online capability"],
143         0x9F15: ["Merchant Category Code", "Classifies the type of business being done by the merchant, represented according to ISO 8583:1993 for Card Acceptor Business Code"],
144         0x9F16: ["Merchant Identifier", "When concatenated with the Acquirer Identifier, uniquely identifies a given merchant"],
145         0x9F17: ["Personal Identification Number (PIN) Try Counter", "Number of PIN tries remaining"],
146         0x9F18: ["Issuer Script Identifier", "Identification of the Issuer Script"],
147         0x9F1A: ["Terminal Country Code", "Indicates the country of the terminal, represented according to ISO 3166"],
148         0x9F1B: ["Terminal Floor Limit", "Indicates the floor limit in the terminal in conjunction with the AID"],
149         0x9F1C: ["Terminal Identification", "Designates the unique location of a terminal at a merchant"],
150         0x9F1D: ["Terminal Risk Management Data", "Application-specific value used by the card for risk management purposes"],
151         0x9F1E: ["Interface Device (IFD) Serial Number", "Unique and permanent serial number assigned to the IFD by the manufacturer"],
152         0x9F1F: ["Track 1 Discretionary Data", "Discretionary part of track 1 according to ISO/IEC 7813"],
153         0x9F20: ["Track 2 Discretionary Data", "Discretionary part of track 2 according to ISO/IEC 7813"],
154         0x9F21: ["Transaction Time", "Local time that the transaction was authorised"],
155         0x9F22: ["Certification Authority Public Key Index", "Identifies the certification authority's public key in conjunction with the RID"],
156         0x9F23: ["Upper Consecutive Offline Limit", "Issuer-specified preference for the maximum number of consecutive offline transactions for this ICC application allowed in a terminal without online capability"],
157         0x9F26: ["Application Cryptogram", "Cryptogram returned by the ICC in response of the GENERATE AC command"],
158         0x9F27: ["Cryptogram Information Data", "Indicates the type of cryptogram and the actions to be performed by the terminal"],
159         0x9F2D: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Certificate", "ICC PIN Encipherment Public Key certified by the issuer"],
160         0x9F2E: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Exponent", "ICC PIN Encipherment Public Key Exponent used for PIN encipherment"],
161         0x9F2F: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Remainder", "Remaining digits of the ICC PIN Encipherment Public Key Modulus"],
162         0x9F32: ["Issuer Public Key Exponent", "Issuer public key exponent used for theverification of the Signed Static Application Data and the ICC Public Key Certificate"],
163         0x9F33: ["Terminal Capabilities", "Indicates the card data input, CVM, and security capabilities of the terminal"],
164         0x9F34: ["Cardholder Verification Method (CVM) Results", "Indicates the results of the last CVM performed"],
165         0x9F35: ["Terminal Type", "Indicates the environment of the terminal, its communications capability, and its operational control"],
166         0x9F36: ["Application Transaction Counter (ATC)", "Counter maintained by the application in the ICC (incrementing the ATC is managed by the ICC)"],
167         0x9F37: ["Unpredictable Number", "Value to provide variability and uniqueness to the generation of a cryptogram"],
168         0x9F38: ["Processing Options Data Object List (PDOL)", "Contains a list of terminal resident data objects (tags and lengths) needed by the ICC in processing the GET PROCESSING OPTIONS command"],
169         0x9F39: ["Point-of-Service (POS) Entry Mode", "Indicates the method by which the PAN was entered, according to the first two digits of the ISO 8583:1987 POS Entry Mode"],
170         0x9F3A: ["Amount, Reference Currency", "Authorised amount expressed in the reference currency"],
171         0x9F3B: ["Application Reference Currency", "1-4 currency codes used between the terminal and the ICC when the Transaction Currency Code is different from the Application Currency Code; each code is 3 digits according to ISO 4217"],
172         0x9F3C: ["Transaction Reference Currency Code", "Code defining the common currency used by the terminal in case the Transaction Currency Code is different from theApplication Currency Code"],
173         0x9F3D: ["Transaction Reference Currency Exponent", "Indicates the implied position of the decimal point from the right of the transaction amount, with the Transaction Reference Currency Code represented according to ISO 4217"],
174         0x9F40: ["Additional Terminal Capabilities", "Indicates the data input and output capabilities of the terminal"],
175         0x9F41: ["Transaction Sequence Counter", "Counter maintained by the terminal that is incremented by one for each transaction"],
176         0x9F42: ["Application Currency Code", "Indicates the currency in which the account is managed according to ISO 4217"],
177         0x9F43: ["Application Reference Currency Exponent", "Indicates the implied position of the decimal point from the right of the amount, for each of the 1-4 reference currencies represented according to ISO 4217"],
178         0x9F44: ["Application Currency Exponent", "Indicates the implied position of the decimal point from the right of the amount represented according to ISO 4217"],
179         0x9F45: ["Data Authentication Code", "An issuer assigned value that is retained by the terminal during the verification process of the Signed Static Application Data"],
180         0x9F46: ["Integrated Circuit Card (ICC) Public Key Certificate", "ICC Public Key certified by the issuer"],
181         0x9F47: ["Integrated Circuit Card (ICC) Public Key Exponent", "ICC Public Key Exponent used for the verification of the Signed Dynamic Application Data"],
182         0x9F48: ["Integrated Circuit Card (ICC) Public Key Remainder", "Remaining digits of the ICC Public Key Modulus"],
183         0x9F49: ["Dynamic Data Authentication Data Object List (DDOL)", "List of data objects (tag and length) to be passed to the ICC in the INTERNAL AUTHENTICATE command"],
184         0x9F4A: ["Static Data Authentication Tag List", "List of tags of primitive data objects defined in this specification whose value fields are to be included in the Signed Static or Dynamic Application Data"],
185         0x9F4B: ["Signed Dynamic Application Data", "Digital signature on critical application parameters for DDA or CDA"],
186         0x9F4C: ["ICC Dynamic Number", "Time-variant number generated by the ICC, to be captured by the terminal"],
187         0x9F4D: ["Log Entry", "Provides the SFI of the Transaction Log file and its number of records"],
188         0x9F4E: ["Merchant Name and Location", "Indicates the name and location of the merchant"],
189         0x9F4F: ["Log Format", "List (in tag and length format) of data objects representing the logged data elements that are passed to the terminal when a transaction  log record is read"],
190         0xA5: ["File Control Information (FCI) Proprietary Template", "Identifies the data object proprietary to this specification in the FCI template according to ISO/IEC 7816-4"],
191         0xBF0C: ["File Control Information (FCI) Issuer Discretionary Data", "Issuer discretionary part of the FCI"],
192     }
193     
194     specialTags = {
195         0x8C: 1,
196         0x8D: 1,
197         0x9F38: 1,
198         0x9F4F: 1,
199     }
200     
201     @staticmethod
202     def decodeTag(data):
203         tag = data.pop()
204         constructed = (tag & SCardTLV.CONSTRUCTED != 0x00)
205         
206         if tag & SCardTLV.EXTENDED == SCardTLV.EXTENDED:
207             tag = (tag << 8) | data.pop()
208             while tag & SCardTLV.MSB != 0x00:
209                 tag = (tag << 8) | data.pop()
210         if tag == 0x9f8004:
211             tag = 0x9f80
212             data.append(0x04)
213         
214         length = data.pop()
215         if length == 0x80:
216             length = -1
217         elif length & SCardTLV.MSB != 0x00:
218             toread = length ^ SCardTLV.MSB
219             length = 0
220             for i in range(toread):
221                 length = (length << 8) | data.pop()
222         
223         return [tag, length, constructed]
224     
225     @staticmethod
226     def decode(data):
227         if isinstance(data, basestring):
228             data = toASCIIBytes(data)
229         
230         data.reverse()
231         
232         ret = {}
233         
234         while len(data) > 0:
235             [tag, length, constructed] = SCardTLV.decodeTag(data)
236             
237             if length == -1:
238                 length = rsearch(data, [0, 0])
239             
240             read = data[-length:]
241             read.reverse()
242             data[-length:] = []
243             
244             if constructed:
245                 read = SCardTLV.decode(read)
246             
247             ret[tag] = read
248         
249         return ret
250     
251     @staticmethod
252     def displayTag(data, intend=""):
253         if data[0] in SCardTLV.tags:
254             print intend + SCardTLV.tags[data[0]][0] + " [" + hex(data[0]) + "], length "+str(data[1])
255         else:
256             print intend + "UNKNOWN TAG [" + hex(data[0]) + "], length "+str(data[1])
257     
258     @staticmethod
259     def display(data, intend="", singleline = None):
260         for tag, value in data.items():
261             if tag in SCardTLV.tags:
262                 print intend + SCardTLV.tags[tag][0] + " [" + hex(tag) + "]:"
263             else:
264                 print intend + "UNKNOWN TAG [" + hex(tag) + "]:"
265             
266             if tag in SCardTLV.specialTags:
267                 value.reverse()
268                 while len(value) > 0:
269                     SCardTLV.displayTag(SCardTLV.decodeTag(value), intend+"\t")
270             elif isinstance(value, dict):
271                 SCardTLV.display(value, intend+"\t")
272             else:
273                 print intend+"\t"+toHexString(value)+" ("+toASCIIString(value)+")"
274
275 class SCardSelectFileBy:
276     MFDFEF = 0x00
277     childDF = 0x01
278     childEF = 0x02
279     parentDF = 0x03
280     DFname = 0x04
281     fromMF = 0x08
282     fromDF = 0x09
283
284 class SCardSelectFileRecord:
285     first = 0x00
286     last = 0x01
287     next = 0x02
288     previous = 0x03
289     
290     FCI = 0x00
291     FCP = 0x04
292     FMD = 0x08
293
294 class SCardReadRecordIdentifier:
295     P1isRecordNumber = 0x04
296     ReadP1 = 0x00
297     ReadP1toLast = 0x01
298     ReadLasttoP1 = 0x02
299     
300     P1isRecordIdentifier = 0x00
301     ReadFirstOccurrence = 0x00
302     ReadLastOccurrence = 0x01
303     ReadNextOccurrence = 0x02
304     ReadPreviousOccurrence = 0x03
305
306 class SCardVerifyCodeType:
307     general = 0x00
308     specific = 0x80
309
310 class SCardVerifyCodeTypeEMV:
311     plaintext = 0x80
312     enciphered = 0x88
313
314 class SCardReader:
315     GET_FIRMWARE_VERSION = 0x2078
316     DISPLAY_LCD_MESSAGE = 0x2079
317     READ_KEY = 0x2080
318     
319     def __init__(self, conn):
320         self.connection = conn
321     
322     def getFeatures(self):
323         print smartcard.pcsc.PCSCPart10.getFeatureRequest(self.connection)
324
325 class SCardEMVPINEncode:
326     @staticmethod
327     def plaintext(digits = ""):
328         if not isinstance(digits, str):
329             raise Exception("PIN must be passed as a string.")
330         
331         if len(digits) > 12:
332             raise Exception("PIN must not be longer than 12 digits.")
333         
334         for i in digits:
335             if not i.isdigit():
336                 raise Exception('PIN must have digits only!')
337         
338         digits = [int(i) for i in digits]
339         digits.reverse()
340         
341         send = []
342         send.append(0x20 | len(digits))
343         
344         while len(digits) > 0:
345             first = digits.pop()
346             if len(digits) > 0:
347                 second = digits.pop()
348             else:
349                 second = 0xf
350             
351             send.append(first << 4 | second)
352         
353         while len(send) < 8:
354             send.append(0xff)
355         
356         return send
357
358 class SCardSecurePIN:
359     bmFormatStringBits = 0x00
360     bmFormatStringBytes = 0x80
361     
362     bmFormatStringPINPositionMask = 0b01111000
363     
364     bmFormatStringJustifyLeft = 0x00
365     bmFormatStringJustifyRight = 0x04
366     
367     bmFormatStringBinary = 0x00
368     bmFormatStringBCD = 0x01
369     bmFormatStringASCII = 0x02
370     
371     def __init__(self, conn):
372         self.connection = conn
373     
374     # TODO: Implement various hardware pin-entry options
375
376 class SCard:
377     def __init__(self, serv):
378         observer = SCardConnectionObserver()
379         serv.connection.addObserver(observer)
380         serv.connection.connect(protocol = smartcard.CardConnection.CardConnection.T0_protocol|smartcard.CardConnection.CardConnection.T1_protocol)
381         exclusive = smartcard.ExclusiveTransmitCardConnection.ExclusiveTransmitCardConnection(serv.connection)
382         exclusive.lock()
383         time.sleep(0.2)
384         print "inserted", toHexString(serv.connection.getATR())
385         
386         self.service = serv
387         self.connection = serv.connection
388         self.exclusive = exclusive
389         self.observer = observer
390     
391     def __del__(self):
392         self.exclusive.unlock()
393         self.connection.deleteObserver(self.observer)
394     
395     def _sendAPDU(self, adpu = []):
396         response, SW1, SW2 = self.exclusive.transmit(adpu)
397         
398         if SW1 != 0x90:
399             raise SCardResponseException(SW1, SW2)
400         
401         return response, SW1, SW2
402     
403     def GetData(self, CLA = 0x00, P1 = 0x00, P2 = 0x00, Le = 0x00):
404         try:
405             return self._sendAPDU([CLA, 0xCA, P1, P2, Le])
406         except SCardResponseException as e:
407             if e.SW1 == 0x6C:
408                 return self.GetData(CLA, P1, P2, Le = e.SW2)
409             else:
410                 raise e
411     
412     def GetProcessingOption(self, CLA = 0x80, TLV = None):
413         adpu = [CLA, 0xA8, 0x00, 0x00]
414         
415         adpu.append(len(TLV))
416         adpu.extend(TLV)
417         
418         adpu.append(0x00)
419         
420         return self._sendAPDU(adpu)
421     
422     def GetResponse(self, CLA = 0x00, P1 = 0x00, P2 = 0x00, Le = 0x00):
423         return self._sendAPDU([CLA, 0xC0, P1, P2, Le])
424     
425     def ReadBinary(self, CLA = 0x00, offset = 0x00, Le = 0x00):
426         try:
427             return self._sendAPDU([CLA, 0xB0, (offset >> 8) & 0x7F, offset & 0xFF, Le])
428         except SCardResponseException as e:
429             if e.SW1 == 0x6C:
430                 return self.ReadBinary(CLA, offset, Le = e.SW2)
431             else:
432                 raise e
433     
434     def ReadRecord(self, CLA = 0x00, record = 0x00, identifier = 0x00, Le = 0x00):
435         try:
436             return self._sendAPDU([CLA, 0xB2, record, identifier, Le])
437         except SCardResponseException as e:
438             if e.SW1 == 0x6C:
439                 return self.ReadRecord(CLA, record, identifier, Le = e.SW2)
440             else:
441                 raise e
442     
443     def SelectFile(self, CLA = 0x00, by = 0x00, record = 0x00, data = None, Le = None):
444         adpu = [CLA, 0xA4, by, record]
445         
446         if isinstance(data, basestring):
447             data = toASCIIBytes(data)
448         
449         if isinstance(data, list):
450             adpu.append(len(data))
451             adpu.extend(data)
452         
453         if Le != None:
454             adpu.append(Le)
455         
456         return self._sendAPDU(adpu)
457     
458     def Verify(self, CLA=0x00, P1 = 0x00, codeType = 0x00, data = None):
459         adpu = [CLA, 0x20, P1, codeType]
460         
461         if isinstance(data, basestring):
462             data = toASCIIBytes(data)
463         
464         if isinstance(data, list):
465             adpu.append(len(data))
466             adpu.extend(data)
467         
468         return self._sendAPDU(adpu)
469
470 class SCardActions:
471     def __init__(self, card):
472         self.card = card
473     
474     def cdRoot(self, CLA = 0x00):
475         return self.cd("3F20")
476     
477     def cdAID(self, AID, CLA = 0x00):
478         return self.card.SelectFile(CLA = CLA, by = SCardSelectFileBy.DFname, data = toBytes(AID), Le = 0)
479     
480     def cd(self, MFEFDF, CLA = 0x00):
481         return self.card.SelectFile(CLA = CLA, by = SCardSelectFileBy.MFDFEF, data = toBytes(MFEFDF), Le = 0)
482     
483     def readBinaryEF(self, EF, blockSize = 4, CLA = 0x00):
484         attrs = self.cd(EF, CLA = CLA)
485         data = []
486         offset = 0
487         try:
488             while True:
489                 offset = int(len(data) / blockSize)
490                 result, _, _ = self.card.ReadBinary(offset = offset, CLA = CLA)
491                 if result == []:
492                     break
493                 data = data[0:offset * blockSize] + result
494         except SCardResponseException as e:
495             if e.SW1 == 0x6B:
496                 pass
497             else:
498                 raise e
499         
500         return data
501
502 class SCardHandler:
503     @staticmethod
504     def getCard():
505         request = smartcard.CardRequest.CardRequest(newcardonly=True, timeout=None)
506         print "Insert card..."
507         service = request.waitforcard()
508         print "Card detected..."
509         time.sleep(0.5)
510         return SCard(service)
511
512 class SCardELS:
513     aids = [
514         "D6160000300101"
515     ]
516     
517     def __init__(self, card):
518         self.card = SCardActions(card)
519     
520     def selectAID(self):
521         for aid in SCardELS.aids:
522             try:
523                 return self.card.cdAID(aid)
524             except:
525                 pass
526         
527         raise Exception('ELS AID not available')
528     
529     def readCert(self):
530         data = self.card.readBinaryEF("0001")
531         return data
532     
533     def readData(self):
534         data = self.card.readBinaryEF("0002")
535         return data
536
537 class SCardEMV:
538     aids = [
539         "A0000000031010",
540         "A0000000032010",
541         "A0000000032020",
542         "A0000000038010",
543         "A0000000041010",
544         "A0000000049999",
545         "A0000000043060",
546         "A0000000046000",
547         "A0000000050001",
548         "A0000000050002",
549         "A00000002501",
550         "A0000005241010",
551         "A0000001523010",
552         "A0000002771010",
553         "A00000006510",
554         "A0000000291010",
555         "A0000001211010",
556         "A0000001410001",
557         "A0000001544442",
558         "A0000003591010028001"
559     ]
560     
561     def __init__(self, card):
562         self.card = card
563     
564     def selectRoot(self):
565         self.card.SelectFile(by = SCardSelectFileBy.MFDFEF, data = toBytes("3F20"))
566     
567     def selectAIDby1PAY(self):
568         try:
569             SCardDebug(SCardDebug.DEBUG, "Trying 1PAY.SYS.DDF01...")
570             data, SW1, SW2 = self.card.SelectFile(by = SCardSelectFileBy.DFname, data = "1PAY.SYS.DDF01", Le = 0x00)
571             SCardDebug(SCardDebug.DEBUG, "... success")
572             
573             fileControl = SCardTLV.decode(data)
574             SCardTLV.display(fileControl);
575             
576             records = []
577             sfi = fileControl[0x6F][0xA5][0x88][0]
578             
579             try:
580                 for i in range(1, 255):
581                     data, SW1, SW2 = self.card.ReadRecord(record = i, identifier = (sfi << 3) | SCardReadRecordIdentifier.P1isRecordNumber | SCardReadRecordIdentifier.ReadP1)
582                     records.append(SCardTLV.decode(data))
583             except SCardResponseException as e:
584                 if e.SW1 != 0x6A or (e.SW2 != 0x83 and e.SW2 != 0x88):
585                     raise SCardResponseException(e.SW1, e.SW2)
586             
587             return self.card.SelectFile(by = SCardSelectFileBy.DFname, data = records[0][0x70][0x61][0x4f], Le = 0x00)
588         except SCardResponseException:
589             SCardDebug(SCardDebug.DEBUG, "1PAY.SYS.DDF01 not supported, skipping...")
590             raise SCardInternalException(SCardInternalException.SCardEMV_1PAY_Not_Supported, "1PAY.SYS.DDF01 not supported")
591     
592     def selectAIDbruteforce(self):
593         for i in SCardEMV.aids:
594             aid = toBytes(i)
595             try:
596                 SCardDebug(SCardDebug.DEBUG, "Trying AID " + i + "...")
597                 out = self.card.SelectFile(by = SCardSelectFileBy.DFname, data = aid, Le = 0x00)
598                 SCardDebug(SCardDebug.DEBUG, "... supported")
599                 return out
600             except SCardResponseException:
601                 SCardDebug(SCardDebug.DEBUG, "... not supported")
602         raise Exception("Cannot select EMV AID")
603     
604     def selectAID(self):
605         #try:
606             #self.selectRoot()
607             #out = self.selectAIDby1PAY()
608         #except SCardInternalException:
609             try:
610                 #self.selectRoot()
611                 out = self.selectAIDbruteforce()
612             except SCardInternalException:
613                 raise Exception("Cannot select EMV AID")
614         
615             return SCardTLV.decode(out[0])
616     
617     def getPDOL(self, AIDdata):
618         PDOL = []
619         if 0x6F in AIDdata:
620             if 0xA5 in AIDdata[0x6F]:
621                 if 0x9F38 in AIDdata[0x6F][0xA5]:
622                     PDOL = emv.decodePDOL(AIDdata[0x6F][0xA5][0x9F38])
623         
624         return PDOL
625     
626     def decodePDOL(self, PDOL=[]):
627         if len(PDOL) == 0:
628             return []
629         
630         PDOL = PDOL[:]
631         PDOL.reverse()
632         
633         tags = []
634         while len(PDOL) > 0:
635             tags.append(SCardTLV.decodeTag(PDOL)[0:2])
636         
637         return tags
638     
639     def getProcessingOptions(self, decodedPDOL=None):
640         toSend = []
641         knownTags = {
642             #0x9F1A: [0x06, 0x16]
643         }
644         
645         for tag in decodedPDOL:
646             temporary = [0]*tag[1]
647             if tag[0] in knownTags:
648                 temporary[0:len(knownTags[tag[0]])] = knownTags[tag[0]]
649                 temporary = temporary[0:tag[1]]
650             toSend += temporary
651         
652         toSend = [0x83, len(toSend)] + toSend
653         
654         SCardDebug(SCardDebug.DEBUG, "Getting processing options...")
655         out = self.card.GetProcessingOption(TLV = toSend)
656         return SCardTLV.decode(out[0])
657     
658     def readRecords(self, sfi = 0x01):
659         try:
660             for i in range(1, 255):
661                 data, SW1, SW2 = self.card.ReadRecord(record = i, identifier = (sfi << 3) | SCardReadRecordIdentifier.P1isRecordNumber | SCardReadRecordIdentifier.ReadP1)
662                 SCardTLV.display(SCardTLV.decode(data))
663         except SCardResponseException as e:
664             if e.SW1 != 0x6A or (e.SW2 != 0x83 and e.SW2 != 0x88):
665                 raise SCardResponseException(e.SW1, e.SW2)
666     
667     def readData(self):
668         tags = [
669             0x9F36,
670             0x9F17,
671             0x9F13,
672             0x9F4F
673         ]
674         
675         for tag in tags:
676             first = tag >> 8
677             second = tag & 255
678             
679             try:
680                 data, SW1, SW2 = self.card.GetData(CLA = 0x80, P1 = first, P2 = second)
681                 SCardTLV.display(SCardTLV.decode(data))
682             except SCardResponseException as e:
683                 if e.SW1 != 0x6A or (e.SW2 != 0x83 and e.SW2 != 0x88):
684                     raise SCardResponseException(e.SW1, e.SW2)
685     
686     def PIN(self, data = ""):
687         data, SW1, SW2 = self.card.Verify(codeType = SCardVerifyCodeTypeEMV.plaintext, data = SCardEMVPINEncode.plaintext(data))
688
689
690 ELS = 0
691
692 if ELS:
693     try:
694         cards = SCardHandler()
695         card = cards.getCard()
696         els = SCardELS(card)
697         
698         els.selectAID()
699         cert = toASCIIString(els.readCert())
700         
701         openssl = subprocess.Popen(['openssl', 'x509', '-text', '-noout', '-inform', 'DER'],
702                 stdin = subprocess.PIPE, stdout = subprocess.PIPE)
703         stdout, stderr = openssl.communicate(cert)
704         
705         print ""
706         print stdout
707         print ""
708         
709         data = decoder.decode(toASCIIString(els.readData()))[0]
710         data_signatureType = data.getComponentByPosition(0)
711         data_signature = data.getComponentByPosition(1)
712         assert(data_signatureType.asTuple() == univ.ObjectIdentifier('1.2.840.113549.1.7.2').asTuple())
713         
714         data_signature_version = data_signature.getComponentByPosition(0)
715         assert(data_signature_version >= 3)
716         data_signature_algorithms = data_signature.getComponentByPosition(1)
717         data_signature_data = data_signature.getComponentByPosition(2)
718         data_signature_info = data_signature.getComponentByPosition(data_signature_version)
719         assert(data_signature_info != None)
720         
721         data_signature_data_type = data_signature_data.getComponentByPosition(0)
722         data_signature_data_data = data_signature_data.getComponentByPosition(1)
723         assert(data_signature_data_type.asTuple() == univ.ObjectIdentifier('1.2.616.1.101.4.1.1.1').asTuple())
724         
725         els = data_signature_data_data.asOctets()
726         els = decoder.decode(els)[0]
727         els_version = els.getComponentByPosition(0)
728         assert(els_version == 1)
729         els_serialNumber = els.getComponentByPosition(1)
730         els_university = els.getComponentByPosition(2)
731         els_surnames = els.getComponentByPosition(3)
732         els_surnames_tuple = [str(els_surnames.getComponentByPosition(i)) for i in range(len(els_surnames))]
733         els_surnames_printable = ' '.join(els_surnames_tuple)
734         els_names = els.getComponentByPosition(4)
735         els_names_tuple = [str(els_names.getComponentByPosition(i)) for i in range(len(els_names))]
736         els_names_printable = ' '.join(els_names_tuple)
737         els_studentNumber = els.getComponentByPosition(5)
738         els_editId = els.getComponentByPosition(6)
739         els_personalNumber = els.getComponentByPosition(7)
740         els_expiry = els.getComponentByPosition(8)
741         
742         print "Uczelnia: " + els_university
743         print "Nazwisko: " + els_surnames_printable
744         print "Imię:     " + els_names_printable
745         print "Album:    " + els_studentNumber
746         print "PESEL:    " + els_personalNumber
747         print "Ważność:  " + els_expiry
748         print "Edycja:   " + els_editId
749     except (KeyboardInterrupt, SystemExit):
750         pass
751     except:
752         traceback.print_exc(file=sys.stdout)
753 else:
754     try:
755         """
756         card = SCardHandler.getCard()
757         reader = SCardReader(card.connection)
758         reader.getFeatures()
759         """
760         
761         cards = SCardHandler()
762         card = cards.getCard()
763         emv = SCardEMV(card)
764         
765         AIDdata = emv.selectAID()
766         SCardTLV.display(AIDdata)
767         
768         PDOL = emv.getPDOL(AIDdata)
769         procOpts = emv.getProcessingOptions(PDOL);
770         SCardTLV.display(procOpts)
771         
772         sfi = 0x01
773         
774         if 0x77 in procOpts:
775             sfi = (procOpts[0x77][0x94][0] >> 3)
776         
777         emv.readRecords(sfi)
778         emv.readData()
779     except (KeyboardInterrupt, SystemExit):
780         pass
781     except:
782         traceback.print_exc(file=sys.stdout)