| | |
| | | # -*- coding: utf-8 -*-
|
| | | from __future__ import print_function
|
| | |
|
| | | import smartcard.CardRequest
|
| | | import smartcard.CardConnectionObserver
|
| | | import smartcard.ExclusiveTransmitCardConnection
|
| | | import smartcard.pcsc.PCSCPart10
|
| | |
|
| | | from smartcard.util import toHexString, toASCIIBytes, toBytes, toASCIIString
|
| | |
|
| | | import collections, sys, traceback, time
|
| | |
|
| | | class SCardDebug:
|
| | | DEBUG = 0
|
| | | WARNING = 1
|
| | | ERROR = 2
|
| | | report = 0
|
| | | def __init__(self, level, message):
|
| | | if level >= SCardDebug.report:
|
| | | print(message)
|
| | |
|
| | | class SCardConnectionObserver(smartcard.CardConnectionObserver.CardConnectionObserver):
|
| | | def update( self, cardconnection, ccevent ):
|
| | | if 'connect' == ccevent.type:
|
| | | SCardDebug(SCardDebug.DEBUG, 'connecting to ' + cardconnection.getReader())
|
| | | elif 'disconnect' == ccevent.type:
|
| | | SCardDebug(SCardDebug.DEBUG, 'disconnecting from ' + cardconnection.getReader())
|
| | | elif 'command' == ccevent.type:
|
| | | if isinstance(ccevent.args[0], collections.Sequence):
|
| | | SCardDebug(SCardDebug.DEBUG, 'send ' + toHexString(ccevent.args[0]))
|
| | | elif 'response' == ccevent.type:
|
| | | if isinstance(ccevent.args[0], collections.Sequence):
|
| | | SCardDebug(SCardDebug.DEBUG, 'recv ' + toHexString(ccevent.args[0]) + " " + toHexString(ccevent.args[-2:]))
|
| | |
|
| | | class SCardInternalException(Exception):
|
| | | SCardEMV_1PAY_Not_Supported = 123514
|
| | | def __init__(self, code, msg):
|
| | | self.code = code
|
| | | self.msg = msg
|
| | | |
| | | def __str__(self):
|
| | | return "Internal Exception " + self.code + ": " + self.msg
|
| | |
|
| | | class SCardResponseException(Exception):
|
| | | errors = {
|
| | | 0x6700 : "Wrong Lc",
|
| | | 0x6984 : "Referenced data invalidated",
|
| | | 0x6A81 : "Function not supported",
|
| | | 0x6A82 : "File not found",
|
| | | 0x6A86 : "Incorrect parameters P1, P2",
|
| | | 0x6A87 : "Lc inconsistent"
|
| | | }
|
| | | def __init__(self, SW1, SW2):
|
| | | self.SW1 = SW1
|
| | | self.SW2 = SW2
|
| | | code = (SW1 << 8) + SW2
|
| | | if code in SCardResponseException.errors:
|
| | | self.msg = SCardResponseException.errors[code]
|
| | | else:
|
| | | self.msg = "Unknown error"
|
| | | def __str__(self):
|
| | | return self.msg
|
| | |
|
| | | class SCardTLV:
|
| | | EXTENDED = 0x1F
|
| | | 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"],
|
| | | 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)"],
|
| | | 0x5A: ["Application Primary Account Number (PAN)", "Valid cardholder account number"],
|
| | | 0x5F20: ["Cardholder Name", "Indicates cardholder name according to ISO 7813"],
|
| | | 0x5F24: ["Application Expiration Date", "Date after which application expires"],
|
| | | 0x5F25: ["Application Effective Date", "Date from which the application may be used"],
|
| | | 0x5F28: ["Issuer Country Code", "Indicates the country of the issuer according to ISO 3166"],
|
| | | 0x5F2A: ["Transaction Currency Code", "Indicates the currency code of the transaction according to ISO 4217"],
|
| | | 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."],
|
| | | 0x5F30: ["Service Code", "Service code as defined in ISO/IEC 7813 for track 1 and track 2"],
|
| | | 0x5F34: ["Application Primary Account Number (PAN) Sequence Number", "Identifies and differentiates cards with the same PAN"],
|
| | | 0x5F36: ["Transaction Currency Exponent", "Indicates the implied position of the decimal point from the right of the transaction amount represented according to ISO 4217"],
|
| | | 0x5F50: ["Issuer URL", "The URL provides the location of the Issuer's Library Server on the Internet."],
|
| | | 0x5F53: ["International Bank Account Number (IBAN)", "Uniquely identifies the account of a customer at a financial institution as defined in ISO 13616."],
|
| | | 0x5F54: ["Bank Identifier Code (BIC)", "Uniquely identifies a bank as defined in ISO 9362."],
|
| | | 0x5F55: ["Issuer Country Code (alpha2 format)", "Indicates the country of the issuer as defined in ISO 3166 (using a 2 character alphabetic code)"],
|
| | | 0x5F56: ["Issuer Country Code (alpha3 format)", "Indicates the country of the issuer as defined in ISO 3166 (using a 3 character alphabetic code)"],
|
| | | 0x61: ["Application Template", "Contains one or more data objects relevant to an application directory entry according to ISO/IEC 7816-5"],
|
| | | 0x6F: ["File Control Information (FCI) Template", "Identifies the FCI template according to ISO/IEC 7816-4"],
|
| | | 0x70: ["EMV Proprietary Template", "Template proprietary to the EMV specification"],
|
| | | 0x71: ["Issuer Script Template 1", "Contains proprietary issuer data for transmission to the ICC before the second GENERATE AC command"],
|
| | | 0x72: ["Issuer Script Template 2", "Contains proprietary issuer data for transmission to the ICC after the second GENERATE AC command"],
|
| | | 0x73: ["Directory Discretionary Template", "Issuer discretionary part of the directory according to ISO/IEC 7816-5"],
|
| | | 0x77: ["Response Message Template Format 2", "Contains the data objects (with tags and lengths) returned by the ICC in response to a command"],
|
| | | 0x80: ["Response Message Template Format 1", "Contains the data objects (without tags and lengths) returned by the ICC in response to a command"],
|
| | | 0x81: ["Amount, Authorised (Binary)", "Authorised amount of the transaction (excluding adjustments)"],
|
| | | 0x82: ["Application Interchange Profile", "Indicates the capabilities of the card to support specific functions in the application"],
|
| | | 0x83: ["Command Template", "Identifies the data field of a command message"],
|
| | | 0x84: ["Dedicated File (DF) Name", "Identifies the name of the DF as described in ISO/IEC 7816-4"],
|
| | | 0x86: ["Issuer Script Command", "Contains a command for transmission to the ICC"],
|
| | | 0x87: ["Application Priority Indicator", "Indicates the priority of a given application or group of applications in a directory"],
|
| | | 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."],
|
| | | 0x89: ["Authorisation Code", "Value generated by the authorisation authority for an approved transaction"],
|
| | | 0x8A: ["Authorisation Response Code", "Code that defines the disposition of a message"],
|
| | | 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"],
|
| | | 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"],
|
| | | 0x8E: ["Cardholder Verification Method (CVM) List", "Identifies a method of verification of the cardholder supported by the application"],
|
| | | 0x8F: ["Certification Authority Public Key Index", "Identifies the certification authority's public key in conjunction with the RID"],
|
| | | 0x90: ["Issuer Public Key Certificate", "Issuer public key certified by a certification authority"],
|
| | | 0x91: ["Issuer Authentication Data", "Data sent to the ICC for online issuer authentication"],
|
| | | 0x92: ["Issuer Public Key Remainder", "Remaining digits of the Issuer Public Key Modulus"],
|
| | | 0x93: ["Signed Static Application Data", "Digital signature on critical application parameters for SDA"],
|
| | | 0x94: ["Application File Locator (AFL)", "Indicates the location (SFI, range of records) of the AEFs related to a given application"],
|
| | | 0x95: ["Terminal Verification Results", "Status of the different functions as seen from the terminal"],
|
| | | 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"],
|
| | | 0x98: ["Transaction Certificate (TC) Hash Value", "Result of a hash function specified in Book 2, Annex B3.1"],
|
| | | 0x99: ["Transaction Personal Identification Number (PIN) Data", "Data entered by the cardholder for the purpose of the PIN verification"],
|
| | | 0x9A: ["Transaction Date", "Local date that the transaction was authorised"],
|
| | | 0x9B: ["Transaction Status Information", "Indicates the functions performed in a transaction"],
|
| | | 0x9C: ["Transaction Type", "Indicates the type of financial transaction, represented by the first two digits of ISO 8583:1987 Processing Code"],
|
| | | 0x9D: ["Directory Definition File (DDF) Name", "Identifies the name of a DF associated with a directory"],
|
| | | 0x9F01: ["Acquirer Identifier", "Uniquely identifies the acquirer within each payment system"],
|
| | | 0x9F02: ["Amount, Authorised (Numeric)", "Authorised amount of the transaction (excluding adjustments)"],
|
| | | 0x9F03: ["Amount, Other (Numeric)", "Secondary amount associated with the transaction representing a cashback amount"],
|
| | | 0x9F04: ["Amount, Other (Binary)", "Secondary amount associated with the transaction representing a cashback amount"],
|
| | | 0x9F05: ["Application Discretionary Data", "Issuer or payment system specified data relating to the application"],
|
| | | 0x9F06: ["Application Identifier (AID) - terminal", "Identifies the application as described in ISO/IEC 7816-5"],
|
| | | 0x9F07: ["Application Usage Control", "Indicates issuer's specified restrictions on the geographic usage and services allowed for the application"],
|
| | | 0x9F08: ["Application Version Number", "Version number assigned by the payment system for the application"],
|
| | | 0x9F09: ["Application Version Number", "Version number assigned by the payment system for the application"],
|
| | | 0x9F0B: ["Cardholder Name Extended", "Indicates the whole cardholder name when greater than 26 characters using the same coding convention as in ISO 7813"],
|
| | | 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"],
|
| | | 0x9F0E: ["Issuer Action Code - Denial", "Specifies the issuer's conditions that cause the denial of a transaction without attempt to go online"],
|
| | | 0x9F0F: ["Issuer Action Code - Online", "Specifies the issuer's conditions that cause a transaction to be transmitted online"],
|
| | | 0x9F10: ["Issuer Application Data", "Contains proprietary application data for transmission to the issuer in an online transaction"],
|
| | | 0x9F11: ["Issuer Code Table Index", "Indicates the code table according to ISO/IEC 8859 for displaying the Application Preferred Name"],
|
| | | 0x9F12: ["Application Preferred Name", "Preferred mnemonic associated with the AID"],
|
| | | 0x9F13: ["Last Online Application Transaction Counter (ATC) Register", "ATC value of the last transaction that went online"],
|
| | | 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"],
|
| | | 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"],
|
| | | 0x9F16: ["Merchant Identifier", "When concatenated with the Acquirer Identifier, uniquely identifies a given merchant"],
|
| | | 0x9F17: ["Personal Identification Number (PIN) Try Counter", "Number of PIN tries remaining"],
|
| | | 0x9F18: ["Issuer Script Identifier", "Identification of the Issuer Script"],
|
| | | 0x9F1A: ["Terminal Country Code", "Indicates the country of the terminal, represented according to ISO 3166"],
|
| | | 0x9F1B: ["Terminal Floor Limit", "Indicates the floor limit in the terminal in conjunction with the AID"],
|
| | | 0x9F1C: ["Terminal Identification", "Designates the unique location of a terminal at a merchant"],
|
| | | 0x9F1D: ["Terminal Risk Management Data", "Application-specific value used by the card for risk management purposes"],
|
| | | 0x9F1E: ["Interface Device (IFD) Serial Number", "Unique and permanent serial number assigned to the IFD by the manufacturer"],
|
| | | 0x9F1F: ["Track 1 Discretionary Data", "Discretionary part of track 1 according to ISO/IEC 7813"],
|
| | | 0x9F20: ["Track 2 Discretionary Data", "Discretionary part of track 2 according to ISO/IEC 7813"],
|
| | | 0x9F21: ["Transaction Time", "Local time that the transaction was authorised"],
|
| | | 0x9F22: ["Certification Authority Public Key Index", "Identifies the certification authority's public key in conjunction with the RID"],
|
| | | 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"],
|
| | | 0x9F26: ["Application Cryptogram", "Cryptogram returned by the ICC in response of the GENERATE AC command"],
|
| | | 0x9F27: ["Cryptogram Information Data", "Indicates the type of cryptogram and the actions to be performed by the terminal"],
|
| | | 0x9F2D: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Certificate", "ICC PIN Encipherment Public Key certified by the issuer"],
|
| | | 0x9F2E: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Exponent", "ICC PIN Encipherment Public Key Exponent used for PIN encipherment"],
|
| | | 0x9F2F: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Remainder", "Remaining digits of the ICC PIN Encipherment Public Key Modulus"],
|
| | | 0x9F32: ["Issuer Public Key Exponent", "Issuer public key exponent used for theverification of the Signed Static Application Data and the ICC Public Key Certificate"],
|
| | | 0x9F33: ["Terminal Capabilities", "Indicates the card data input, CVM, and security capabilities of the terminal"],
|
| | | 0x9F34: ["Cardholder Verification Method (CVM) Results", "Indicates the results of the last CVM performed"],
|
| | | 0x9F35: ["Terminal Type", "Indicates the environment of the terminal, its communications capability, and its operational control"],
|
| | | 0x9F36: ["Application Transaction Counter (ATC)", "Counter maintained by the application in the ICC (incrementing the ATC is managed by the ICC)"],
|
| | | 0x9F37: ["Unpredictable Number", "Value to provide variability and uniqueness to the generation of a cryptogram"],
|
| | | 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"],
|
| | | 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"],
|
| | | 0x9F3A: ["Amount, Reference Currency", "Authorised amount expressed in the reference currency"],
|
| | | 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"],
|
| | | 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"],
|
| | | 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"],
|
| | | 0x9F40: ["Additional Terminal Capabilities", "Indicates the data input and output capabilities of the terminal"],
|
| | | 0x9F41: ["Transaction Sequence Counter", "Counter maintained by the terminal that is incremented by one for each transaction"],
|
| | | 0x9F42: ["Application Currency Code", "Indicates the currency in which the account is managed according to ISO 4217"],
|
| | | 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"],
|
| | | 0x9F44: ["Application Currency Exponent", "Indicates the implied position of the decimal point from the right of the amount represented according to ISO 4217"],
|
| | | 0x9F45: ["Data Authentication Code", "An issuer assigned value that is retained by the terminal during the verification process of the Signed Static Application Data"],
|
| | | 0x9F46: ["Integrated Circuit Card (ICC) Public Key Certificate", "ICC Public Key certified by the issuer"],
|
| | | 0x9F47: ["Integrated Circuit Card (ICC) Public Key Exponent", "ICC Public Key Exponent used for the verification of the Signed Dynamic Application Data"],
|
| | | 0x9F48: ["Integrated Circuit Card (ICC) Public Key Remainder", "Remaining digits of the ICC Public Key Modulus"],
|
| | | 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"],
|
| | | 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"],
|
| | | 0x9F4B: ["Signed Dynamic Application Data", "Digital signature on critical application parameters for DDA or CDA"],
|
| | | 0x9F4C: ["ICC Dynamic Number", "Time-variant number generated by the ICC, to be captured by the terminal"],
|
| | | 0x9F4D: ["Log Entry", "Provides the SFI of the Transaction Log file and its number of records"],
|
| | | 0x9F4E: ["Merchant Name and Location", "Indicates the name and location of the merchant"],
|
| | | 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"],
|
| | | 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"],
|
| | | 0xBF0C: ["File Control Information (FCI) Issuer Discretionary Data", "Issuer discretionary part of the FCI"],
|
| | | }
|
| | | |
| | | specialTags = {
|
| | | 0x8C: 1,
|
| | | 0x8D: 1,
|
| | | 0x9F38: 1,
|
| | | 0x9F4F: 1,
|
| | | }
|
| | | |
| | | @staticmethod
|
| | | def decodeTag(data):
|
| | | tag = data.pop()
|
| | | constructed = (tag & SCardTLV.CONSTRUCTED != 0x00)
|
| | | |
| | | if tag & SCardTLV.EXTENDED == SCardTLV.EXTENDED:
|
| | | tag = (tag << 8) | data.pop()
|
| | | while tag & SCardTLV.MSB != 0x00:
|
| | | tag = (tag << 8) | data.pop()
|
| | | if tag == 0x9f8004:
|
| | | tag = 0x9f80
|
| | | data.append(0x04)
|
| | | if SCardTLV.debug:
|
| | | print('Got tag: ' + toHexString(tag))
|
| | | |
| | | length = data.pop()
|
| | | if length == 0x80:
|
| | | length = -1
|
| | | elif length & SCardTLV.MSB != 0x00:
|
| | | toread = length ^ SCardTLV.MSB
|
| | | length = 0
|
| | | for i in range(toread):
|
| | | length = (length << 8) | data.pop()
|
| | | |
| | | return [tag, length, constructed]
|
| | | |
| | | @staticmethod
|
| | | def decode(data):
|
| | | if isinstance(data, basestring):
|
| | | data = toASCIIBytes(data)
|
| | | |
| | | data.reverse()
|
| | | |
| | | ret = {}
|
| | | |
| | | while len(data) > 0:
|
| | | [tag, length, constructed] = SCardTLV.decodeTag(data)
|
| | | |
| | | if length == -1:
|
| | | length = rsearch(data, [0, 0])
|
| | | |
| | | read = data[-length:]
|
| | | read.reverse()
|
| | | data[-length:] = []
|
| | | |
| | | if constructed:
|
| | | read = SCardTLV.decode(read)
|
| | | |
| | | if tag in ret:
|
| | | ret[tag].push(read)
|
| | | else:
|
| | | 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]))
|
| | | else:
|
| | | 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) + "]:")
|
| | | else:
|
| | | print(intend + "UNKNOWN TAG [" + hex(tag) + "]:")
|
| | | |
| | | if tag in SCardTLV.specialTags:
|
| | | value.reverse()
|
| | | while len(value) > 0:
|
| | | SCardTLV.displayTag(SCardTLV.decodeTag(value), intend+"\t")
|
| | | elif isinstance(value, dict):
|
| | | SCardTLV.display(value, intend+"\t")
|
| | | else:
|
| | | print(intend+"\t"+toHexString(value)+" ("+toASCIIString(value)+")")
|
| | |
|
| | | class SCardSelectFileBy:
|
| | | MFDFEF = 0x00
|
| | | childDF = 0x01
|
| | | childEF = 0x02
|
| | | parentDF = 0x03
|
| | | DFname = 0x04
|
| | | fromMF = 0x08
|
| | | fromDF = 0x09
|
| | |
|
| | | class SCardSelectFileRecord:
|
| | | first = 0x00
|
| | | last = 0x01
|
| | | next = 0x02
|
| | | previous = 0x03
|
| | | |
| | | FCI = 0x00
|
| | | FCP = 0x04
|
| | | FMD = 0x08
|
| | |
|
| | | class SCardReadRecordIdentifier:
|
| | | P1isRecordNumber = 0x04
|
| | | ReadP1 = 0x00
|
| | | ReadP1toLast = 0x01
|
| | | ReadLasttoP1 = 0x02
|
| | | |
| | | P1isRecordIdentifier = 0x00
|
| | | ReadFirstOccurrence = 0x00
|
| | | ReadLastOccurrence = 0x01
|
| | | ReadNextOccurrence = 0x02
|
| | | ReadPreviousOccurrence = 0x03
|
| | |
|
| | | class SCardVerifyCodeType:
|
| | | general = 0x00
|
| | | specific = 0x80
|
| | |
|
| | | class SCardVerifyCodeTypeEMV:
|
| | | plaintext = 0x80
|
| | | enciphered = 0x88
|
| | |
|
| | | class SCardReader:
|
| | | GET_FIRMWARE_VERSION = 0x2078
|
| | | DISPLAY_LCD_MESSAGE = 0x2079
|
| | | READ_KEY = 0x2080
|
| | | |
| | | def __init__(self, conn):
|
| | | self.connection = conn
|
| | | |
| | | def getFeatures(self):
|
| | | print(smartcard.pcsc.PCSCPart10.getFeatureRequest(self.connection))
|
| | |
|
| | | class SCardEMVPINEncode:
|
| | | @staticmethod
|
| | | def plaintext(digits = ""):
|
| | | if not isinstance(digits, str):
|
| | | raise Exception("PIN must be passed as a string.")
|
| | | |
| | | if len(digits) > 12:
|
| | | raise Exception("PIN must not be longer than 12 digits.")
|
| | | |
| | | for i in digits:
|
| | | if not i.isdigit():
|
| | | raise Exception('PIN must have digits only!')
|
| | | |
| | | digits = [int(i) for i in digits]
|
| | | digits.reverse()
|
| | | |
| | | send = []
|
| | | send.append(0x20 | len(digits))
|
| | | |
| | | while len(digits) > 0:
|
| | | first = digits.pop()
|
| | | if len(digits) > 0:
|
| | | second = digits.pop()
|
| | | else:
|
| | | second = 0xf
|
| | | |
| | | send.append(first << 4 | second)
|
| | | |
| | | while len(send) < 8:
|
| | | send.append(0xff)
|
| | | |
| | | return send
|
| | |
|
| | | class SCardSecurePIN:
|
| | | bmFormatStringBits = 0x00
|
| | | bmFormatStringBytes = 0x80
|
| | | |
| | | bmFormatStringPINPositionMask = 0b01111000
|
| | | |
| | | bmFormatStringJustifyLeft = 0x00
|
| | | bmFormatStringJustifyRight = 0x04
|
| | | |
| | | bmFormatStringBinary = 0x00
|
| | | bmFormatStringBCD = 0x01
|
| | | bmFormatStringASCII = 0x02
|
| | | |
| | | def __init__(self, conn):
|
| | | self.connection = conn
|
| | | |
| | | # TODO: Implement various hardware pin-entry options
|
| | |
|
| | | class SCard:
|
| | | def __init__(self, serv):
|
| | | observer = SCardConnectionObserver()
|
| | | serv.connection.addObserver(observer)
|
| | | serv.connection.connect(protocol = smartcard.CardConnection.CardConnection.T0_protocol|smartcard.CardConnection.CardConnection.T1_protocol)
|
| | | exclusive = smartcard.ExclusiveTransmitCardConnection.ExclusiveTransmitCardConnection(serv.connection)
|
| | | exclusive.lock()
|
| | | time.sleep(0.2)
|
| | | print("inserted", toHexString(serv.connection.getATR()))
|
| | | |
| | | self.service = serv
|
| | | self.connection = serv.connection
|
| | | self.exclusive = exclusive
|
| | | self.observer = observer
|
| | | |
| | | def __del__(self):
|
| | | self.exclusive.unlock()
|
| | | self.connection.deleteObserver(self.observer)
|
| | | |
| | | def _sendAPDU(self, adpu = []):
|
| | | response, SW1, SW2 = self.exclusive.transmit(adpu)
|
| | | if SW1 != 0x90:
|
| | | raise SCardResponseException(SW1, SW2)
|
| | | |
| | | return response, SW1, SW2
|
| | | |
| | | def GetData(self, CLA = 0x00, P1 = 0x00, P2 = 0x00, Le = 0x00):
|
| | | try:
|
| | | return self._sendAPDU([CLA, 0xCA, P1, P2, Le])
|
| | | except SCardResponseException as e:
|
| | | if e.SW1 == 0x6C:
|
| | | return self.GetData(CLA, P1, P2, Le = e.SW2)
|
| | | else:
|
| | | raise e
|
| | | |
| | | def GetProcessingOption(self, CLA = 0x80, TLV = None):
|
| | | adpu = [CLA, 0xA8, 0x00, 0x00]
|
| | | |
| | | adpu.append(len(TLV))
|
| | | adpu.extend(TLV)
|
| | | |
| | | adpu.append(0x00)
|
| | | |
| | | return self._sendAPDU(adpu)
|
| | | |
| | | def ReadBinary(self, CLA = 0x00, offset = 0x00, Le = 0x00):
|
| | | try:
|
| | | return self._sendAPDU([CLA, 0xB0, (offset >> 8) & 0x7F, offset & 0xFF, Le])
|
| | | except SCardResponseException as e:
|
| | | if e.SW1 == 0x6C:
|
| | | 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:
|
| | | return self._sendAPDU([CLA, 0xB2, record, identifier, Le])
|
| | | except SCardResponseException as e:
|
| | | if e.SW1 == 0x6C:
|
| | | return self.ReadRecord(CLA, record, identifier, Le = e.SW2)
|
| | | else:
|
| | | raise e
|
| | | |
| | | def SelectFile(self, CLA = 0x00, by = 0x00, record = 0x00, data = None, Le = None):
|
| | | adpu = [CLA, 0xA4, by, record]
|
| | | |
| | | if isinstance(data, basestring):
|
| | | data = toASCIIBytes(data)
|
| | | |
| | | if isinstance(data, list):
|
| | | adpu.append(len(data))
|
| | | adpu.extend(data)
|
| | | |
| | | if Le != None:
|
| | | adpu.append(Le)
|
| | | |
| | | return self._sendAPDU(adpu)
|
| | | |
| | | def Verify(self, CLA=0x00, P1 = 0x00, codeType = 0x00, data = None):
|
| | | adpu = [CLA, 0x20, P1, codeType]
|
| | | |
| | | if isinstance(data, basestring):
|
| | | data = toASCIIBytes(data)
|
| | | |
| | | if isinstance(data, list):
|
| | | adpu.append(len(data))
|
| | | adpu.extend(data)
|
| | | |
| | | return self._sendAPDU(adpu)
|
| | |
|
| | | class SCardHandler:
|
| | | @staticmethod
|
| | | def getCard():
|
| | | request = smartcard.CardRequest.CardRequest(newcardonly=True, timeout=None)
|
| | | print("Insert card...")
|
| | | service = request.waitforcard()
|
| | | time.sleep(0.5)
|
| | | return SCard(service)
|
| | |
|
| | | class SCardELS:
|
| | | aids = [
|
| | | "D6160000300101"
|
| | | ]
|
| | | |
| | | def __init__(self, card):
|
| | | self.card = 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
|
| | | except:
|
| | | 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()
|
| | | |
| | | def readData(self):
|
| | | self.card.SelectFile(by = SCardSelectFileBy.childEF, data = toBytes("0002"), Le = 0)
|
| | | return self.card.ReadBinaryAll()
|
| | |
|
| | | class SCardEMV:
|
| | | aids = [
|
| | | "A0000000031010",
|
| | | "A0000000032010",
|
| | | "A0000000032020",
|
| | | "A0000000038010",
|
| | | "A0000000041010",
|
| | | "A0000000049999",
|
| | | "A0000000043060",
|
| | | "A0000000046000",
|
| | | "A0000000050001",
|
| | | "A0000000050002",
|
| | | "A00000002501",
|
| | | "A0000005241010",
|
| | | "A0000001523010",
|
| | | "A0000002771010",
|
| | | "A00000006510",
|
| | | "A0000000291010",
|
| | | "A0000001211010",
|
| | | "A0000001410001",
|
| | | "A0000001544442",
|
| | | "A0000003591010028001"
|
| | | ]
|
| | | |
| | | def __init__(self, card):
|
| | | self.card = card
|
| | | |
| | | def selectRoot(self):
|
| | | self.card.SelectFile(by = SCardSelectFileBy.MFDFEF, data = toBytes("3F20"))
|
| | | |
| | | def selectAIDby1PAY(self):
|
| | | try:
|
| | | SCardDebug(SCardDebug.DEBUG, "Trying 1PAY.SYS.DDF01...")
|
| | | data, SW1, SW2 = self.card.SelectFile(by = SCardSelectFileBy.DFname, data = "1PAY.SYS.DDF01", Le = 0x00)
|
| | | SCardDebug(SCardDebug.DEBUG, "... success")
|
| | | |
| | | fileControl = SCardTLV.decode(data)
|
| | | SCardTLV.display(fileControl);
|
| | | |
| | | records = []
|
| | | sfi = fileControl[0x6F][0xA5][0x88][0]
|
| | | |
| | | try:
|
| | | for i in range(1, 255):
|
| | | 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:
|
| | | raise SCardResponseException(e.SW1, e.SW2)
|
| | | |
| | | return self.card.SelectFile(by = SCardSelectFileBy.DFname, data = records[0][0x70][0x61][0x4f], Le = 0x00)
|
| | | except SCardResponseException:
|
| | | SCardDebug(SCardDebug.DEBUG, "1PAY.SYS.DDF01 not supported, skipping...")
|
| | | raise SCardInternalException(SCardInternalException.SCardEMV_1PAY_Not_Supported, "1PAY.SYS.DDF01 not supported")
|
| | | |
| | | def selectAIDbruteforce(self):
|
| | | for i in SCardEMV.aids:
|
| | | aid = toBytes(i)
|
| | | try:
|
| | | SCardDebug(SCardDebug.DEBUG, "Trying AID " + i + "...")
|
| | | out = self.card.SelectFile(by = SCardSelectFileBy.DFname, data = aid, Le = 0x00)
|
| | | SCardDebug(SCardDebug.DEBUG, "... supported")
|
| | | return out
|
| | | except SCardResponseException:
|
| | | SCardDebug(SCardDebug.DEBUG, "... not supported")
|
| | | raise Exception("Cannot select EMV AID")
|
| | | |
| | | def selectAID(self):
|
| | | #try:
|
| | | #self.selectRoot()
|
| | | #out = self.selectAIDby1PAY()
|
| | | #except SCardInternalException:
|
| | | try:
|
| | | #self.selectRoot()
|
| | | out = self.selectAIDbruteforce()
|
| | | except SCardInternalException:
|
| | | raise Exception("Cannot select EMV AID")
|
| | | |
| | | return SCardTLV.decode(out[0])
|
| | | |
| | | def getPDOL(self, AIDdata):
|
| | | PDOL = []
|
| | | if 0x6F in AIDdata:
|
| | | if 0xA5 in AIDdata[0x6F]:
|
| | | if 0x9F38 in AIDdata[0x6F][0xA5]:
|
| | | PDOL = emv.decodePDOL(AIDdata[0x6F][0xA5][0x9F38])
|
| | | |
| | | return PDOL
|
| | | |
| | | def decodePDOL(self, PDOL=[]):
|
| | | if len(PDOL) == 0:
|
| | | return []
|
| | | |
| | | PDOL = PDOL[:]
|
| | | PDOL.reverse()
|
| | | |
| | | tags = []
|
| | | while len(PDOL) > 0:
|
| | | tags.append(SCardTLV.decodeTag(PDOL)[0:2])
|
| | | |
| | | return tags
|
| | | |
| | | def getProcessingOptions(self, decodedPDOL=None):
|
| | | toSend = []
|
| | | knownTags = {
|
| | | #0x9F1A: [0x06, 0x16]
|
| | | }
|
| | | |
| | | for tag in decodedPDOL:
|
| | | temporary = [0]*tag[1]
|
| | | if tag[0] in knownTags:
|
| | | temporary[0:len(knownTags[tag[0]])] = knownTags[tag[0]]
|
| | | temporary = temporary[0:tag[1]]
|
| | | toSend += temporary
|
| | | |
| | | toSend = [0x83, len(toSend)] + toSend
|
| | | |
| | | SCardDebug(SCardDebug.DEBUG, "Getting processing options...")
|
| | | out = self.card.GetProcessingOption(TLV = toSend)
|
| | | return SCardTLV.decode(out[0])
|
| | | |
| | | def readRecords(self, sfi = 0x01):
|
| | | try:
|
| | | for i in range(1, 255):
|
| | | 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:
|
| | | raise SCardResponseException(e.SW1, e.SW2)
|
| | | |
| | | def readData(self):
|
| | | tags = [
|
| | | 0x9F36,
|
| | | 0x9F17,
|
| | | 0x9F13,
|
| | | 0x9F4F
|
| | | ]
|
| | | |
| | | for tag in tags:
|
| | | first = tag >> 8
|
| | | second = tag & 255
|
| | | |
| | | data, SW1, SW2 = self.card.GetData(CLA = 0x80, P1 = first, P2 = second)
|
| | | SCardTLV.display(SCardTLV.decode(data))
|
| | | |
| | | def PIN(self, data = ""):
|
| | | data, SW1, SW2 = self.card.Verify(codeType = SCardVerifyCodeTypeEMV.plaintext, data = SCardEMVPINEncode.plaintext(data))
|
| | |
|
| | |
|
| | | ELS = 1
|
| | |
|
| | | if ELS:
|
| | | try:
|
| | | cards = SCardHandler()
|
| | | card = cards.getCard()
|
| | | els = SCardELS(card)
|
| | | |
| | | els.selectAID()
|
| | | SCardTLV.decode(els.readCert())
|
| | | SCardTLV.decode(els.readData())
|
| | | except (KeyboardInterrupt, SystemExit):
|
| | | pass
|
| | | except:
|
| | | traceback.print_exc(file=sys.stdout)
|
| | | else:
|
| | | try:
|
| | | """
|
| | | card = SCardHandler.getCard()
|
| | | reader = SCardReader(card.connection)
|
| | | reader.getFeatures()
|
| | | """
|
| | | |
| | | cards = SCardHandler()
|
| | | card = cards.getCard()
|
| | | emv = SCardEMV(card)
|
| | | |
| | | AIDdata = emv.selectAID()
|
| | | SCardTLV.display(AIDdata)
|
| | | |
| | | PDOL = emv.getPDOL(AIDdata)
|
| | | procOpts = emv.getProcessingOptions(PDOL);
|
| | | SCardTLV.display(procOpts)
|
| | | |
| | | sfi = 0x01
|
| | | |
| | | if 0x77 in procOpts:
|
| | | sfi = (procOpts[0x77][0x94][0] >> 3)
|
| | | |
| | | emv.readRecords(sfi)
|
| | | emv.readData()
|
| | | except (KeyboardInterrupt, SystemExit):
|
| | | pass
|
| | | except:
|
| | | traceback.print_exc(file=sys.stdout)
|
| | | # -*- coding: utf-8 -*- |
| | | 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 |
| | | |
| | | import collections, sys, traceback, time |
| | | |
| | | class SCardDebug: |
| | | DEBUG = 0 |
| | | WARNING = 1 |
| | | ERROR = 2 |
| | | report = 0 |
| | | def __init__(self, level, message): |
| | | if level >= SCardDebug.report: |
| | | print message |
| | | |
| | | class SCardConnectionObserver(smartcard.CardConnectionObserver.CardConnectionObserver): |
| | | def update( self, cardconnection, ccevent ): |
| | | if 'connect' == ccevent.type: |
| | | SCardDebug(SCardDebug.DEBUG, 'connecting to ' + cardconnection.getReader()) |
| | | elif 'disconnect' == ccevent.type: |
| | | SCardDebug(SCardDebug.DEBUG, 'disconnecting from ' + cardconnection.getReader()) |
| | | elif 'command' == ccevent.type: |
| | | if isinstance(ccevent.args[0], collections.Sequence): |
| | | SCardDebug(SCardDebug.DEBUG, 'send ' + toHexString(ccevent.args[0])) |
| | | elif 'response' == ccevent.type: |
| | | if isinstance(ccevent.args[0], collections.Sequence): |
| | | SCardDebug(SCardDebug.DEBUG, 'recv ' + toHexString(ccevent.args[0]) + " " + toHexString(ccevent.args[-2:])) |
| | | |
| | | class SCardInternalException(Exception): |
| | | SCardEMV_1PAY_Not_Supported = 123514 |
| | | def __init__(self, code, msg): |
| | | self.code = code |
| | | self.msg = msg |
| | | |
| | | def __str__(self): |
| | | return "Internal Exception " + self.code + ": " + self.msg |
| | | |
| | | class SCardResponseException(Exception): |
| | | errors = { |
| | | 0x6700 : "Wrong Lc", |
| | | 0x6984 : "Referenced data invalidated", |
| | | 0x6A81 : "Function not supported", |
| | | 0x6A82 : "File not found", |
| | | 0x6A86 : "Incorrect parameters P1, P2", |
| | | 0x6A87 : "Lc inconsistent" |
| | | } |
| | | def __init__(self, SW1, SW2): |
| | | self.SW1 = SW1 |
| | | self.SW2 = SW2 |
| | | code = (SW1 << 8) + SW2 |
| | | if code in SCardResponseException.errors: |
| | | self.msg = SCardResponseException.errors[code] |
| | | else: |
| | | self.msg = "Unknown error" |
| | | def __str__(self): |
| | | return self.msg |
| | | |
| | | class SCardTLV: |
| | | EXTENDED = 0x1F |
| | | MSB = 0x80 |
| | | CONSTRUCTED = 0x20 |
| | | |
| | | tags = { |
| | | 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"], |
| | | 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)"], |
| | | 0x5A: ["Application Primary Account Number (PAN)", "Valid cardholder account number"], |
| | | 0x5F20: ["Cardholder Name", "Indicates cardholder name according to ISO 7813"], |
| | | 0x5F24: ["Application Expiration Date", "Date after which application expires"], |
| | | 0x5F25: ["Application Effective Date", "Date from which the application may be used"], |
| | | 0x5F28: ["Issuer Country Code", "Indicates the country of the issuer according to ISO 3166"], |
| | | 0x5F2A: ["Transaction Currency Code", "Indicates the currency code of the transaction according to ISO 4217"], |
| | | 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."], |
| | | 0x5F30: ["Service Code", "Service code as defined in ISO/IEC 7813 for track 1 and track 2"], |
| | | 0x5F34: ["Application Primary Account Number (PAN) Sequence Number", "Identifies and differentiates cards with the same PAN"], |
| | | 0x5F36: ["Transaction Currency Exponent", "Indicates the implied position of the decimal point from the right of the transaction amount represented according to ISO 4217"], |
| | | 0x5F50: ["Issuer URL", "The URL provides the location of the Issuer's Library Server on the Internet."], |
| | | 0x5F53: ["International Bank Account Number (IBAN)", "Uniquely identifies the account of a customer at a financial institution as defined in ISO 13616."], |
| | | 0x5F54: ["Bank Identifier Code (BIC)", "Uniquely identifies a bank as defined in ISO 9362."], |
| | | 0x5F55: ["Issuer Country Code (alpha2 format)", "Indicates the country of the issuer as defined in ISO 3166 (using a 2 character alphabetic code)"], |
| | | 0x5F56: ["Issuer Country Code (alpha3 format)", "Indicates the country of the issuer as defined in ISO 3166 (using a 3 character alphabetic code)"], |
| | | 0x61: ["Application Template", "Contains one or more data objects relevant to an application directory entry according to ISO/IEC 7816-5"], |
| | | 0x6F: ["File Control Information (FCI) Template", "Identifies the FCI template according to ISO/IEC 7816-4"], |
| | | 0x70: ["EMV Proprietary Template", "Template proprietary to the EMV specification"], |
| | | 0x71: ["Issuer Script Template 1", "Contains proprietary issuer data for transmission to the ICC before the second GENERATE AC command"], |
| | | 0x72: ["Issuer Script Template 2", "Contains proprietary issuer data for transmission to the ICC after the second GENERATE AC command"], |
| | | 0x73: ["Directory Discretionary Template", "Issuer discretionary part of the directory according to ISO/IEC 7816-5"], |
| | | 0x77: ["Response Message Template Format 2", "Contains the data objects (with tags and lengths) returned by the ICC in response to a command"], |
| | | 0x80: ["Response Message Template Format 1", "Contains the data objects (without tags and lengths) returned by the ICC in response to a command"], |
| | | 0x81: ["Amount, Authorised (Binary)", "Authorised amount of the transaction (excluding adjustments)"], |
| | | 0x82: ["Application Interchange Profile", "Indicates the capabilities of the card to support specific functions in the application"], |
| | | 0x83: ["Command Template", "Identifies the data field of a command message"], |
| | | 0x84: ["Dedicated File (DF) Name", "Identifies the name of the DF as described in ISO/IEC 7816-4"], |
| | | 0x86: ["Issuer Script Command", "Contains a command for transmission to the ICC"], |
| | | 0x87: ["Application Priority Indicator", "Indicates the priority of a given application or group of applications in a directory"], |
| | | 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."], |
| | | 0x89: ["Authorisation Code", "Value generated by the authorisation authority for an approved transaction"], |
| | | 0x8A: ["Authorisation Response Code", "Code that defines the disposition of a message"], |
| | | 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"], |
| | | 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"], |
| | | 0x8E: ["Cardholder Verification Method (CVM) List", "Identifies a method of verification of the cardholder supported by the application"], |
| | | 0x8F: ["Certification Authority Public Key Index", "Identifies the certification authority's public key in conjunction with the RID"], |
| | | 0x90: ["Issuer Public Key Certificate", "Issuer public key certified by a certification authority"], |
| | | 0x91: ["Issuer Authentication Data", "Data sent to the ICC for online issuer authentication"], |
| | | 0x92: ["Issuer Public Key Remainder", "Remaining digits of the Issuer Public Key Modulus"], |
| | | 0x93: ["Signed Static Application Data", "Digital signature on critical application parameters for SDA"], |
| | | 0x94: ["Application File Locator (AFL)", "Indicates the location (SFI, range of records) of the AEFs related to a given application"], |
| | | 0x95: ["Terminal Verification Results", "Status of the different functions as seen from the terminal"], |
| | | 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"], |
| | | 0x98: ["Transaction Certificate (TC) Hash Value", "Result of a hash function specified in Book 2, Annex B3.1"], |
| | | 0x99: ["Transaction Personal Identification Number (PIN) Data", "Data entered by the cardholder for the purpose of the PIN verification"], |
| | | 0x9A: ["Transaction Date", "Local date that the transaction was authorised"], |
| | | 0x9B: ["Transaction Status Information", "Indicates the functions performed in a transaction"], |
| | | 0x9C: ["Transaction Type", "Indicates the type of financial transaction, represented by the first two digits of ISO 8583:1987 Processing Code"], |
| | | 0x9D: ["Directory Definition File (DDF) Name", "Identifies the name of a DF associated with a directory"], |
| | | 0x9F01: ["Acquirer Identifier", "Uniquely identifies the acquirer within each payment system"], |
| | | 0x9F02: ["Amount, Authorised (Numeric)", "Authorised amount of the transaction (excluding adjustments)"], |
| | | 0x9F03: ["Amount, Other (Numeric)", "Secondary amount associated with the transaction representing a cashback amount"], |
| | | 0x9F04: ["Amount, Other (Binary)", "Secondary amount associated with the transaction representing a cashback amount"], |
| | | 0x9F05: ["Application Discretionary Data", "Issuer or payment system specified data relating to the application"], |
| | | 0x9F06: ["Application Identifier (AID) - terminal", "Identifies the application as described in ISO/IEC 7816-5"], |
| | | 0x9F07: ["Application Usage Control", "Indicates issuer's specified restrictions on the geographic usage and services allowed for the application"], |
| | | 0x9F08: ["Application Version Number", "Version number assigned by the payment system for the application"], |
| | | 0x9F09: ["Application Version Number", "Version number assigned by the payment system for the application"], |
| | | 0x9F0B: ["Cardholder Name Extended", "Indicates the whole cardholder name when greater than 26 characters using the same coding convention as in ISO 7813"], |
| | | 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"], |
| | | 0x9F0E: ["Issuer Action Code - Denial", "Specifies the issuer's conditions that cause the denial of a transaction without attempt to go online"], |
| | | 0x9F0F: ["Issuer Action Code - Online", "Specifies the issuer's conditions that cause a transaction to be transmitted online"], |
| | | 0x9F10: ["Issuer Application Data", "Contains proprietary application data for transmission to the issuer in an online transaction"], |
| | | 0x9F11: ["Issuer Code Table Index", "Indicates the code table according to ISO/IEC 8859 for displaying the Application Preferred Name"], |
| | | 0x9F12: ["Application Preferred Name", "Preferred mnemonic associated with the AID"], |
| | | 0x9F13: ["Last Online Application Transaction Counter (ATC) Register", "ATC value of the last transaction that went online"], |
| | | 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"], |
| | | 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"], |
| | | 0x9F16: ["Merchant Identifier", "When concatenated with the Acquirer Identifier, uniquely identifies a given merchant"], |
| | | 0x9F17: ["Personal Identification Number (PIN) Try Counter", "Number of PIN tries remaining"], |
| | | 0x9F18: ["Issuer Script Identifier", "Identification of the Issuer Script"], |
| | | 0x9F1A: ["Terminal Country Code", "Indicates the country of the terminal, represented according to ISO 3166"], |
| | | 0x9F1B: ["Terminal Floor Limit", "Indicates the floor limit in the terminal in conjunction with the AID"], |
| | | 0x9F1C: ["Terminal Identification", "Designates the unique location of a terminal at a merchant"], |
| | | 0x9F1D: ["Terminal Risk Management Data", "Application-specific value used by the card for risk management purposes"], |
| | | 0x9F1E: ["Interface Device (IFD) Serial Number", "Unique and permanent serial number assigned to the IFD by the manufacturer"], |
| | | 0x9F1F: ["Track 1 Discretionary Data", "Discretionary part of track 1 according to ISO/IEC 7813"], |
| | | 0x9F20: ["Track 2 Discretionary Data", "Discretionary part of track 2 according to ISO/IEC 7813"], |
| | | 0x9F21: ["Transaction Time", "Local time that the transaction was authorised"], |
| | | 0x9F22: ["Certification Authority Public Key Index", "Identifies the certification authority's public key in conjunction with the RID"], |
| | | 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"], |
| | | 0x9F26: ["Application Cryptogram", "Cryptogram returned by the ICC in response of the GENERATE AC command"], |
| | | 0x9F27: ["Cryptogram Information Data", "Indicates the type of cryptogram and the actions to be performed by the terminal"], |
| | | 0x9F2D: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Certificate", "ICC PIN Encipherment Public Key certified by the issuer"], |
| | | 0x9F2E: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Exponent", "ICC PIN Encipherment Public Key Exponent used for PIN encipherment"], |
| | | 0x9F2F: ["Integrated Circuit Card (ICC) PIN Encipherment Public Key Remainder", "Remaining digits of the ICC PIN Encipherment Public Key Modulus"], |
| | | 0x9F32: ["Issuer Public Key Exponent", "Issuer public key exponent used for theverification of the Signed Static Application Data and the ICC Public Key Certificate"], |
| | | 0x9F33: ["Terminal Capabilities", "Indicates the card data input, CVM, and security capabilities of the terminal"], |
| | | 0x9F34: ["Cardholder Verification Method (CVM) Results", "Indicates the results of the last CVM performed"], |
| | | 0x9F35: ["Terminal Type", "Indicates the environment of the terminal, its communications capability, and its operational control"], |
| | | 0x9F36: ["Application Transaction Counter (ATC)", "Counter maintained by the application in the ICC (incrementing the ATC is managed by the ICC)"], |
| | | 0x9F37: ["Unpredictable Number", "Value to provide variability and uniqueness to the generation of a cryptogram"], |
| | | 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"], |
| | | 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"], |
| | | 0x9F3A: ["Amount, Reference Currency", "Authorised amount expressed in the reference currency"], |
| | | 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"], |
| | | 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"], |
| | | 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"], |
| | | 0x9F40: ["Additional Terminal Capabilities", "Indicates the data input and output capabilities of the terminal"], |
| | | 0x9F41: ["Transaction Sequence Counter", "Counter maintained by the terminal that is incremented by one for each transaction"], |
| | | 0x9F42: ["Application Currency Code", "Indicates the currency in which the account is managed according to ISO 4217"], |
| | | 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"], |
| | | 0x9F44: ["Application Currency Exponent", "Indicates the implied position of the decimal point from the right of the amount represented according to ISO 4217"], |
| | | 0x9F45: ["Data Authentication Code", "An issuer assigned value that is retained by the terminal during the verification process of the Signed Static Application Data"], |
| | | 0x9F46: ["Integrated Circuit Card (ICC) Public Key Certificate", "ICC Public Key certified by the issuer"], |
| | | 0x9F47: ["Integrated Circuit Card (ICC) Public Key Exponent", "ICC Public Key Exponent used for the verification of the Signed Dynamic Application Data"], |
| | | 0x9F48: ["Integrated Circuit Card (ICC) Public Key Remainder", "Remaining digits of the ICC Public Key Modulus"], |
| | | 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"], |
| | | 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"], |
| | | 0x9F4B: ["Signed Dynamic Application Data", "Digital signature on critical application parameters for DDA or CDA"], |
| | | 0x9F4C: ["ICC Dynamic Number", "Time-variant number generated by the ICC, to be captured by the terminal"], |
| | | 0x9F4D: ["Log Entry", "Provides the SFI of the Transaction Log file and its number of records"], |
| | | 0x9F4E: ["Merchant Name and Location", "Indicates the name and location of the merchant"], |
| | | 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"], |
| | | 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"], |
| | | 0xBF0C: ["File Control Information (FCI) Issuer Discretionary Data", "Issuer discretionary part of the FCI"], |
| | | } |
| | | |
| | | specialTags = { |
| | | 0x8C: 1, |
| | | 0x8D: 1, |
| | | 0x9F38: 1, |
| | | 0x9F4F: 1, |
| | | } |
| | | |
| | | @staticmethod |
| | | def decodeTag(data): |
| | | tag = data.pop() |
| | | constructed = (tag & SCardTLV.CONSTRUCTED != 0x00) |
| | | |
| | | if tag & SCardTLV.EXTENDED == SCardTLV.EXTENDED: |
| | | tag = (tag << 8) | data.pop() |
| | | while tag & SCardTLV.MSB != 0x00: |
| | | tag = (tag << 8) | data.pop() |
| | | if tag == 0x9f8004: |
| | | tag = 0x9f80 |
| | | data.append(0x04) |
| | | |
| | | length = data.pop() |
| | | if length == 0x80: |
| | | length = -1 |
| | | elif length & SCardTLV.MSB != 0x00: |
| | | toread = length ^ SCardTLV.MSB |
| | | length = 0 |
| | | for i in range(toread): |
| | | length = (length << 8) | data.pop() |
| | | |
| | | return [tag, length, constructed] |
| | | |
| | | @staticmethod |
| | | def decode(data): |
| | | if isinstance(data, basestring): |
| | | data = toASCIIBytes(data) |
| | | |
| | | data.reverse() |
| | | |
| | | ret = {} |
| | | |
| | | while len(data) > 0: |
| | | [tag, length, constructed] = SCardTLV.decodeTag(data) |
| | | |
| | | if length == -1: |
| | | length = rsearch(data, [0, 0]) |
| | | |
| | | read = data[-length:] |
| | | read.reverse() |
| | | data[-length:] = [] |
| | | |
| | | if constructed: |
| | | read = SCardTLV.decode(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]) |
| | | else: |
| | | 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) + "]:" |
| | | else: |
| | | print intend + "UNKNOWN TAG [" + hex(tag) + "]:" |
| | | |
| | | if tag in SCardTLV.specialTags: |
| | | value.reverse() |
| | | while len(value) > 0: |
| | | SCardTLV.displayTag(SCardTLV.decodeTag(value), intend+"\t") |
| | | elif isinstance(value, dict): |
| | | SCardTLV.display(value, intend+"\t") |
| | | else: |
| | | print intend+"\t"+toHexString(value)+" ("+toASCIIString(value)+")" |
| | | |
| | | class SCardSelectFileBy: |
| | | MFDFEF = 0x00 |
| | | childDF = 0x01 |
| | | childEF = 0x02 |
| | | parentDF = 0x03 |
| | | DFname = 0x04 |
| | | fromMF = 0x08 |
| | | fromDF = 0x09 |
| | | |
| | | class SCardSelectFileRecord: |
| | | first = 0x00 |
| | | last = 0x01 |
| | | next = 0x02 |
| | | previous = 0x03 |
| | | |
| | | FCI = 0x00 |
| | | FCP = 0x04 |
| | | FMD = 0x08 |
| | | |
| | | class SCardReadRecordIdentifier: |
| | | P1isRecordNumber = 0x04 |
| | | ReadP1 = 0x00 |
| | | ReadP1toLast = 0x01 |
| | | ReadLasttoP1 = 0x02 |
| | | |
| | | P1isRecordIdentifier = 0x00 |
| | | ReadFirstOccurrence = 0x00 |
| | | ReadLastOccurrence = 0x01 |
| | | ReadNextOccurrence = 0x02 |
| | | ReadPreviousOccurrence = 0x03 |
| | | |
| | | class SCardVerifyCodeType: |
| | | general = 0x00 |
| | | specific = 0x80 |
| | | |
| | | class SCardVerifyCodeTypeEMV: |
| | | plaintext = 0x80 |
| | | enciphered = 0x88 |
| | | |
| | | class SCardReader: |
| | | GET_FIRMWARE_VERSION = 0x2078 |
| | | DISPLAY_LCD_MESSAGE = 0x2079 |
| | | READ_KEY = 0x2080 |
| | | |
| | | def __init__(self, conn): |
| | | self.connection = conn |
| | | |
| | | def getFeatures(self): |
| | | print smartcard.pcsc.PCSCPart10.getFeatureRequest(self.connection) |
| | | |
| | | class SCardEMVPINEncode: |
| | | @staticmethod |
| | | def plaintext(digits = ""): |
| | | if not isinstance(digits, str): |
| | | raise Exception("PIN must be passed as a string.") |
| | | |
| | | if len(digits) > 12: |
| | | raise Exception("PIN must not be longer than 12 digits.") |
| | | |
| | | for i in digits: |
| | | if not i.isdigit(): |
| | | raise Exception('PIN must have digits only!') |
| | | |
| | | digits = [int(i) for i in digits] |
| | | digits.reverse() |
| | | |
| | | send = [] |
| | | send.append(0x20 | len(digits)) |
| | | |
| | | while len(digits) > 0: |
| | | first = digits.pop() |
| | | if len(digits) > 0: |
| | | second = digits.pop() |
| | | else: |
| | | second = 0xf |
| | | |
| | | send.append(first << 4 | second) |
| | | |
| | | while len(send) < 8: |
| | | send.append(0xff) |
| | | |
| | | return send |
| | | |
| | | class SCardSecurePIN: |
| | | bmFormatStringBits = 0x00 |
| | | bmFormatStringBytes = 0x80 |
| | | |
| | | bmFormatStringPINPositionMask = 0b01111000 |
| | | |
| | | bmFormatStringJustifyLeft = 0x00 |
| | | bmFormatStringJustifyRight = 0x04 |
| | | |
| | | bmFormatStringBinary = 0x00 |
| | | bmFormatStringBCD = 0x01 |
| | | bmFormatStringASCII = 0x02 |
| | | |
| | | def __init__(self, conn): |
| | | self.connection = conn |
| | | |
| | | # TODO: Implement various hardware pin-entry options |
| | | |
| | | class SCard: |
| | | def __init__(self, serv): |
| | | observer = SCardConnectionObserver() |
| | | serv.connection.addObserver(observer) |
| | | serv.connection.connect(protocol = smartcard.CardConnection.CardConnection.T0_protocol|smartcard.CardConnection.CardConnection.T1_protocol) |
| | | exclusive = smartcard.ExclusiveTransmitCardConnection.ExclusiveTransmitCardConnection(serv.connection) |
| | | exclusive.lock() |
| | | time.sleep(0.2) |
| | | print "inserted", toHexString(serv.connection.getATR()) |
| | | |
| | | self.service = serv |
| | | self.connection = serv.connection |
| | | self.exclusive = exclusive |
| | | self.observer = observer |
| | | |
| | | def __del__(self): |
| | | self.exclusive.unlock() |
| | | self.connection.deleteObserver(self.observer) |
| | | |
| | | def _sendAPDU(self, adpu = []): |
| | | response, SW1, SW2 = self.exclusive.transmit(adpu) |
| | | |
| | | if SW1 != 0x90: |
| | | raise SCardResponseException(SW1, SW2) |
| | | |
| | | return response, SW1, SW2 |
| | | |
| | | def GetData(self, CLA = 0x00, P1 = 0x00, P2 = 0x00, Le = 0x00): |
| | | try: |
| | | return self._sendAPDU([CLA, 0xCA, P1, P2, Le]) |
| | | except SCardResponseException as e: |
| | | if e.SW1 == 0x6C: |
| | | return self.GetData(CLA, P1, P2, Le = e.SW2) |
| | | else: |
| | | raise e |
| | | |
| | | def GetProcessingOption(self, CLA = 0x80, TLV = None): |
| | | adpu = [CLA, 0xA8, 0x00, 0x00] |
| | | |
| | | adpu.append(len(TLV)) |
| | | adpu.extend(TLV) |
| | | |
| | | adpu.append(0x00) |
| | | |
| | | 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]) |
| | | except SCardResponseException as e: |
| | | if e.SW1 == 0x6C: |
| | | return self.ReadBinary(CLA, offset, Le = e.SW2) |
| | | else: |
| | | raise e |
| | | |
| | | def ReadRecord(self, CLA = 0x00, record = 0x00, identifier = 0x00, Le = 0x00): |
| | | try: |
| | | return self._sendAPDU([CLA, 0xB2, record, identifier, Le]) |
| | | except SCardResponseException as e: |
| | | if e.SW1 == 0x6C: |
| | | return self.ReadRecord(CLA, record, identifier, Le = e.SW2) |
| | | else: |
| | | raise e |
| | | |
| | | def SelectFile(self, CLA = 0x00, by = 0x00, record = 0x00, data = None, Le = None): |
| | | adpu = [CLA, 0xA4, by, record] |
| | | |
| | | if isinstance(data, basestring): |
| | | data = toASCIIBytes(data) |
| | | |
| | | if isinstance(data, list): |
| | | adpu.append(len(data)) |
| | | adpu.extend(data) |
| | | |
| | | if Le != None: |
| | | adpu.append(Le) |
| | | |
| | | return self._sendAPDU(adpu) |
| | | |
| | | def Verify(self, CLA=0x00, P1 = 0x00, codeType = 0x00, data = None): |
| | | adpu = [CLA, 0x20, P1, codeType] |
| | | |
| | | if isinstance(data, basestring): |
| | | data = toASCIIBytes(data) |
| | | |
| | | if isinstance(data, list): |
| | | adpu.append(len(data)) |
| | | adpu.extend(data) |
| | | |
| | | 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..." |
| | | service = request.waitforcard() |
| | | print "Card detected..." |
| | | time.sleep(0.5) |
| | | return SCard(service) |
| | | |
| | | class SCardELS: |
| | | aids = [ |
| | | "D6160000300101" |
| | | ] |
| | | |
| | | def __init__(self, card): |
| | | self.card = SCardActions(card) |
| | | |
| | | def selectAID(self): |
| | | for aid in SCardELS.aids: |
| | | try: |
| | | return self.card.cdAID(aid) |
| | | except: |
| | | pass |
| | | |
| | | raise Exception('ELS AID not available') |
| | | |
| | | def readCert(self): |
| | | data = self.card.readBinaryEF("0001") |
| | | return data |
| | | |
| | | def readData(self): |
| | | data = self.card.readBinaryEF("0002") |
| | | return data |
| | | |
| | | class SCardEMV: |
| | | aids = [ |
| | | "A0000000031010", |
| | | "A0000000032010", |
| | | "A0000000032020", |
| | | "A0000000038010", |
| | | "A0000000041010", |
| | | "A0000000049999", |
| | | "A0000000043060", |
| | | "A0000000046000", |
| | | "A0000000050001", |
| | | "A0000000050002", |
| | | "A00000002501", |
| | | "A0000005241010", |
| | | "A0000001523010", |
| | | "A0000002771010", |
| | | "A00000006510", |
| | | "A0000000291010", |
| | | "A0000001211010", |
| | | "A0000001410001", |
| | | "A0000001544442", |
| | | "A0000003591010028001" |
| | | ] |
| | | |
| | | def __init__(self, card): |
| | | self.card = card |
| | | |
| | | def selectRoot(self): |
| | | self.card.SelectFile(by = SCardSelectFileBy.MFDFEF, data = toBytes("3F20")) |
| | | |
| | | def selectAIDby1PAY(self): |
| | | try: |
| | | SCardDebug(SCardDebug.DEBUG, "Trying 1PAY.SYS.DDF01...") |
| | | data, SW1, SW2 = self.card.SelectFile(by = SCardSelectFileBy.DFname, data = "1PAY.SYS.DDF01", Le = 0x00) |
| | | SCardDebug(SCardDebug.DEBUG, "... success") |
| | | |
| | | fileControl = SCardTLV.decode(data) |
| | | SCardTLV.display(fileControl); |
| | | |
| | | records = [] |
| | | sfi = fileControl[0x6F][0xA5][0x88][0] |
| | | |
| | | try: |
| | | for i in range(1, 255): |
| | | 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 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) |
| | | except SCardResponseException: |
| | | SCardDebug(SCardDebug.DEBUG, "1PAY.SYS.DDF01 not supported, skipping...") |
| | | raise SCardInternalException(SCardInternalException.SCardEMV_1PAY_Not_Supported, "1PAY.SYS.DDF01 not supported") |
| | | |
| | | def selectAIDbruteforce(self): |
| | | for i in SCardEMV.aids: |
| | | aid = toBytes(i) |
| | | try: |
| | | SCardDebug(SCardDebug.DEBUG, "Trying AID " + i + "...") |
| | | out = self.card.SelectFile(by = SCardSelectFileBy.DFname, data = aid, Le = 0x00) |
| | | SCardDebug(SCardDebug.DEBUG, "... supported") |
| | | return out |
| | | except SCardResponseException: |
| | | SCardDebug(SCardDebug.DEBUG, "... not supported") |
| | | raise Exception("Cannot select EMV AID") |
| | | |
| | | def selectAID(self): |
| | | #try: |
| | | #self.selectRoot() |
| | | #out = self.selectAIDby1PAY() |
| | | #except SCardInternalException: |
| | | try: |
| | | #self.selectRoot() |
| | | out = self.selectAIDbruteforce() |
| | | except SCardInternalException: |
| | | raise Exception("Cannot select EMV AID") |
| | | |
| | | return SCardTLV.decode(out[0]) |
| | | |
| | | def getPDOL(self, AIDdata): |
| | | PDOL = [] |
| | | if 0x6F in AIDdata: |
| | | if 0xA5 in AIDdata[0x6F]: |
| | | if 0x9F38 in AIDdata[0x6F][0xA5]: |
| | | PDOL = emv.decodePDOL(AIDdata[0x6F][0xA5][0x9F38]) |
| | | |
| | | return PDOL |
| | | |
| | | def decodePDOL(self, PDOL=[]): |
| | | if len(PDOL) == 0: |
| | | return [] |
| | | |
| | | PDOL = PDOL[:] |
| | | PDOL.reverse() |
| | | |
| | | tags = [] |
| | | while len(PDOL) > 0: |
| | | tags.append(SCardTLV.decodeTag(PDOL)[0:2]) |
| | | |
| | | return tags |
| | | |
| | | def getProcessingOptions(self, decodedPDOL=None): |
| | | toSend = [] |
| | | knownTags = { |
| | | #0x9F1A: [0x06, 0x16] |
| | | } |
| | | |
| | | for tag in decodedPDOL: |
| | | temporary = [0]*tag[1] |
| | | if tag[0] in knownTags: |
| | | temporary[0:len(knownTags[tag[0]])] = knownTags[tag[0]] |
| | | temporary = temporary[0:tag[1]] |
| | | toSend += temporary |
| | | |
| | | toSend = [0x83, len(toSend)] + toSend |
| | | |
| | | SCardDebug(SCardDebug.DEBUG, "Getting processing options...") |
| | | out = self.card.GetProcessingOption(TLV = toSend) |
| | | return SCardTLV.decode(out[0]) |
| | | |
| | | def readRecords(self, sfi = 0x01): |
| | | try: |
| | | for i in range(1, 255): |
| | | 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 and e.SW2 != 0x88): |
| | | raise SCardResponseException(e.SW1, e.SW2) |
| | | |
| | | def readData(self): |
| | | tags = [ |
| | | 0x9F36, |
| | | 0x9F17, |
| | | 0x9F13, |
| | | 0x9F4F |
| | | ] |
| | | |
| | | for tag in tags: |
| | | 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 = 0 |
| | | |
| | | if ELS: |
| | | try: |
| | | cards = SCardHandler() |
| | | card = cards.getCard() |
| | | els = SCardELS(card) |
| | | |
| | | els.selectAID() |
| | | 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: |
| | | traceback.print_exc(file=sys.stdout) |
| | | else: |
| | | try: |
| | | """ |
| | | card = SCardHandler.getCard() |
| | | reader = SCardReader(card.connection) |
| | | reader.getFeatures() |
| | | """ |
| | | |
| | | cards = SCardHandler() |
| | | card = cards.getCard() |
| | | emv = SCardEMV(card) |
| | | |
| | | AIDdata = emv.selectAID() |
| | | SCardTLV.display(AIDdata) |
| | | |
| | | PDOL = emv.getPDOL(AIDdata) |
| | | procOpts = emv.getProcessingOptions(PDOL); |
| | | SCardTLV.display(procOpts) |
| | | |
| | | sfi = 0x01 |
| | | |
| | | if 0x77 in procOpts: |
| | | sfi = (procOpts[0x77][0x94][0] >> 3) |
| | | |
| | | emv.readRecords(sfi) |
| | | emv.readData() |
| | | except (KeyboardInterrupt, SystemExit): |
| | | pass |
| | | except: |
| | | traceback.print_exc(file=sys.stdout) |