Skip to content
This repository was archived by the owner on Mar 13, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 96 additions & 2 deletions BRcommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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
Expand All @@ -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
12 changes: 12 additions & 0 deletions BRutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions BadReader.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?

Expand Down