diff --git a/BRcommands.py b/BRcommands.py index ac07f4b..1d8a8ce 100644 --- a/BRcommands.py +++ b/BRcommands.py @@ -21,7 +21,8 @@ # @version PoC 2 from time import sleep -from smartcard.util import toHexString +import BRutils as utils +from smartcard.util import toHexString, toBytes, toASCIIString # While I wrap my head around Abstract classes for Python let's assume all classes are "well-made" @@ -65,11 +66,14 @@ class Help: # @details Prints each command's help description in alphabetical order # @param cardService unused parameter # @param cmdList unused parameter + # @todo Make display a specific help message for read and write def execute(cardService, cmdList): print('\t' + Disconnect.helpDesc) print('\t' + Exit.helpDesc) - print('\t' + Help.helpDesc) print('\t' + GetATR.helpDesc) + print('\t' + Help.helpDesc) + print('\t' + Read.helpDesc) + print('\t' + Verify.helpDesc) ## @brief Class defining the command to display the current card's ATR # @details Contains a description for the help command and an implementation of the execute() method to display the card's Answer To Reset @@ -83,3 +87,93 @@ class GetATR: # @todo Ensure that the cardService object actually has a connection or protect against cases where it doesn't def execute(cardService, cmdList): print(toHexString(cardService.connection.getATR())) # TODO : Check that there actually is a connection in the CardService object + +## @brief Class defining the command to display the current card's content +#  @details Contains a description for the help command and an implementation of the execute() method to display the card's content +# @todo Improve the help description look +# @todo Write a specific detailed help display function to be used by "help read" +class Read: + + helpDesc = "read : read the current card's content, see \"help read\" for details" # TODO : emphasize in some way the "help read" command + + ## @brief Public implementation of the execute() method to read a card's content + # @details Parses the command entered by the user in the prompt to check if it should translate to ASCII or not and to retrieve the first address to read and the length + # @note If no length is given then will read the card until its end + # @param cardService a CardService object with a connection to the card to read + # @param cmdList the list of commands entered by the user + def execute(cardService, cmdList): + + if cmdList[1] == "--ascii": + startAddrHex = int(hex(int(cmdList[2])),16) + + if len(cmdList) == 4: # check if length set or should read all + readLenHex = int(hex(int(cmdList[3])),16) + else: + readLenHex = int(hex(256 - int(cmdList[2])),16) + + displayAscii = True + + else: + startAddrHex = int(hex(int(cmdList[1])),16) + + if len(cmdList) == 3: # check if length set or should read all + readLenHex = int(hex(int(cmdList[2])),16) + else: + readLenHex = int(hex(256 - int(cmdList[1])),16) + + displayAscii = False + + apdu = utils.apdu.READ_MEMORY_CARD + [startAddrHex] + [readLenHex] + response, sw1, sw2 = cardService.connection.transmit(apdu) + + if displayAscii: + print(toASCIIString(response)) + else: + print(toHexString(response)) + print("SW : " + hex(sw1) + " " + hex(sw2)) + +## @brief Class defining the command to verify a given PIN against the card's +#  @details Contains a description for the help command and an implementation of the execute() method to try to verify the PIN +# @todo Write an extended help display function to explain the parameters +class Verify: + + helpDesc = "verify : checks a given PIN against the one securing the card, if they match the card becomes writable, see \"help verify\" for details" + + ## @brief Public implementation of the execute() method to try and verify a PIN against the one used by the card + # @details Parses the command sent by the user to check if it should use the default PIN, otherwise reads the three ASCII character PIN and translates it to hex before having it checked by the card + # @note Assumes the PIN is in one "word" and that it is ASCII to be translated into hex + # @param cardService a CardService object with a connection to a PIN protected card + # @param cmdList the list of the commands entered by the user in the prompt + # @todo Color the error messages + def execute(cardService, cmdList): + + if cmdList[1] == "--default": + pinToCheck = [0xFF,0xFF,0xFF] + + else: + + if len(cmdList[1]) != 3: + print("ERROR : PIN must be exactly 3 characters long !") # TODO : put in red + return + else: + pinToCheck = [] + for i in range(3): + pinToCheck.append(int(hex(ord(cmdList[1][0])),16)) + + apdu = utils.apdu.PRESENT_CODE_MEMORY_CARD + pinToCheck + response, sw1, sw2 = cardService.connection.transmit(apdu) + + print("DEBUG :") + print(apdu) + print(response) + print(sw1) + print(sw2) + + if sw1 == 0x90 and sw2 == 0x07: + print("SUCCESS : The card is not write protected anymore !") # TODO : put in green + elif sw1 == 0x90 and sw2 == 0x00: + print("ERROR : The card has been permanently blocked (too many failed attempts)") # TODO : put in red + elif sw1 == 0x90: + print("ERROR : Incorrect PIN !") # TODO : put in red + else: + print("ERROR : Unknown error on verification !") # TODO : put in red diff --git a/BRutils.py b/BRutils.py index e59300f..dd3abd3 100644 --- a/BRutils.py +++ b/BRutils.py @@ -26,3 +26,15 @@ class const: # Statuses for the prompt STATUS_PROMPT_DISCONNECTED = 10 STATUS_PROMPT_EXITING = 20 + +## @brief Class containing APDUs to transmit +# @sa https://hashtips.files.wordpress.com/2013/10/pma-acr38xccid-6-02.pdf +class apdu: + SELECT_CARD_TYPE = [0xFF,0xA4,0x00,0x00,0x01,0x06] + READ_MEMORY_CARD = [0xFF,0xB0,0x00] # + byte address of first byte to read + length of data to read (in bytes ?) + READ_PRESENTATION_ERROR_COUNTER_MEMORY_CARD = [0xFF,0xB1,0x00,0x00,0x04] + READ_PROTECTION_BITS = [0xFF,0xB2,0x00,0x00,0x04] + WRITE_MEMORY_CARD = [0xFF,0xD0,0x00] # + byte address of first byte to write + length of data to write (in bytes ?) + every byte to write + WRITE_PROTECTION_MEMORY_CARD = [0xFF,0xD1,0x00] # + byte address of first byte to write (from 0x00 to 0x1F) + length of data to write (in bytes ?) + every byte to write + PRESENT_CODE_MEMORY_CARD = [0xFF,0x20,0x00,0x00,0x03] # + each of the three bytes of the PIN + CHANGE_CODE_MEMORY_CARD = [0xFF,0xD2,0x00,0x01,0x03] # + each of the three bytes of the new PIN diff --git a/BadReader.py b/BadReader.py old mode 100644 new mode 100755 index 95dce6a..d6b0993 --- a/BadReader.py +++ b/BadReader.py @@ -50,14 +50,23 @@ def enterPrompt(cardService): requestedDisconnect = True requestedExit = True # Required to exit the prompt for reconnection as is cmds.Disconnect.execute(cardService, cmdList) + elif cmdList[0] == "exit": if not requestedDisconnect: print("Disconnecting card before exiting") # TODO : Yellow ? cmds.Disconnect.execute(cardService, cmdList) print("Exiting") requestedExit = True + elif cmdList[0] == "getATR": cmds.GetATR.execute(cardService, cmdList) + + elif cmdList[0] == "read": + cmds.Read.execute(cardService, cmdList) + + elif cmdList[0] == "verify": + cmds.Verify.execute(cardService, cmdList) + else: # default to help() cmds.Help.execute(cardService, cmdList) # TODO : add specific message when there's a typo ?