Big change: Replace SW_OK and SW1_RETRY with a more generic check_sw() that gets its data from STATUS_MAP
Small improvements in brutefid, including the ability to brute subdirectories More fixes for passport secure messaging, using check_sw, can now decrypt error responses ("End of file reached before Le bytes read", etc.) Add support for tuples/lists in dictionaries in MERGE_DICTS_RECURSIVE in cards/__init__.py git-svn-id: svn+ssh://localhost/home/henryk/svn/cyberflex-shell/trunk@177 f711b948-2313-0410-aaa9-d29f33439f0b
This commit is contained in:
parent
f01836762b
commit
d685765fa0
19
brutefid.py
19
brutefid.py
|
@ -86,7 +86,9 @@ if __name__ == "__main__":
|
|||
del exit_now
|
||||
|
||||
if len(arguments) > 0:
|
||||
top_level = binascii.unhexlify("".join( ["".join(e.split()) for e in arguments] ))
|
||||
top_level = ("".join( ["".join(e.split()) for e in arguments] )).split("/")
|
||||
top_level = [binascii.unhexlify(e) for e in top_level]
|
||||
|
||||
|
||||
pycsc_card = connect(reader)
|
||||
card = cards.new_card_object(pycsc_card)
|
||||
|
@ -96,10 +98,11 @@ if __name__ == "__main__":
|
|||
|
||||
card.change_dir()
|
||||
if top_level is not None:
|
||||
card.change_dir(top_level)
|
||||
for e in top_level: card.change_dir(e)
|
||||
|
||||
#objective = (0x2f00, 0x5015) ## Test cases on an OpenSC formatted PKCS#15 card
|
||||
objective = range(0xffff)
|
||||
objective = range(0xffff+1)
|
||||
#objective = range(0x3fff+1) + range(0x7000,0x7fff+1) + range(0xc000,0xd4ff+1) + range(0xd600+1,0xd7ff+1) + range(0xdc00+1,0xffff+1)
|
||||
for fid in objective:
|
||||
data = chr(fid >> 8) + chr(fid & 0xff)
|
||||
if loop % STATUS_INTERVAL == 0:
|
||||
|
@ -113,16 +116,16 @@ if __name__ == "__main__":
|
|||
loop = loop + 1
|
||||
|
||||
result = card.change_dir(data)
|
||||
if result.sw == card.SW_OK:
|
||||
if card.check_sw(result.sw):
|
||||
results_dir[fid] = result
|
||||
card.change_dir()
|
||||
if top_level is not None:
|
||||
card.change_dir(top_level)
|
||||
for e in top_level: card.change_dir(e)
|
||||
|
||||
print >>sys.stderr, "\rDir %04X -> %02X%02X %s" % (fid, result.sw1, result.sw2, status),
|
||||
|
||||
result = card.open_file(data)
|
||||
if result.sw == card.SW_OK:
|
||||
if card.check_sw(result.sw):
|
||||
results_file[fid] = result
|
||||
|
||||
print >>sys.stderr, "\rFile %04X -> %02X%02X %s" % (fid, result.sw1, result.sw2, status),
|
||||
|
@ -140,12 +143,12 @@ if __name__ == "__main__":
|
|||
print "Dir\t%04X" % fid
|
||||
if len(result.data) > 0:
|
||||
print utils.hexdump(result.data)
|
||||
print TLV_utils.decode(result.data)
|
||||
print TLV_utils.decode(result.data,tags=card.TLV_OBJECTS)
|
||||
|
||||
for fid, result in results_file.items():
|
||||
print "-"*80
|
||||
print "File\t%04X" % fid
|
||||
if len(result.data) > 0:
|
||||
print utils.hexdump(result.data)
|
||||
print TLV_utils.decode(result.data)
|
||||
print TLV_utils.decode(result.data,tags=card.TLV_OBJECTS)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ class Cardmultiplexer:
|
|||
of the participating classes instead of overriding them."""
|
||||
|
||||
MERGE_DICTS = ("APPLICATIONS", "COMMANDS", "STATUS_WORDS", "VENDORS")
|
||||
MERGE_DICTS_RECURSIVE = ("TLV_OBJECTS", )
|
||||
MERGE_DICTS_RECURSIVE = ("TLV_OBJECTS", "STATUS_MAP")
|
||||
MERGE_LISTS = ()
|
||||
|
||||
def __init__(self, classes, *args, **kwargs):
|
||||
|
@ -146,9 +146,8 @@ class Cardmultiplexer:
|
|||
def _merge_attributes(self):
|
||||
"""Update the local copy of merged attributes."""
|
||||
|
||||
is_descendant = lambda a,b: b in _inspect.getmro(a)
|
||||
ordered_classes = list(self._classes)
|
||||
ordered_classes.sort(cmp=lambda a,b: (a!=b and ( is_descendant(a,b) and 1 or -1 ) or 0))
|
||||
ordered_classes.sort(cmp=lambda a,b: (a!=b and ( issubclass(a,b) and -1 or 1 ) or 0))
|
||||
|
||||
for attr in self.MERGE_DICTS + self.MERGE_DICTS_RECURSIVE:
|
||||
tmpdict = {}
|
||||
|
@ -165,6 +164,10 @@ class Cardmultiplexer:
|
|||
recurse( target[key], value )
|
||||
elif isinstance(value, dict):
|
||||
target[key] = dict(value)
|
||||
elif isinstance(value, (tuple,list)) and isinstance(target[key], list):
|
||||
target[key].extend( [e for e in value if not e in target[key]] )
|
||||
elif isinstance(value, (tuple,list)) and isinstance(target[key], tuple):
|
||||
target[key] = tuple( list(target[key]) + [e for e in value if not e in target[key]] )
|
||||
else:
|
||||
target[key] = value
|
||||
else:
|
||||
|
|
|
@ -117,7 +117,7 @@ class Cyberflex_Card(Java_Card):
|
|||
self.session_key_mac = None
|
||||
|
||||
result = self.send_apdu(apdu)
|
||||
if result.sw != self.SW_OK:
|
||||
if not self.check_sw(result.sw):
|
||||
raise Exception, "Statusword after InitializeUpdate was %s. Warning: No successful ExternalAuthenticate; keyset might be locked soon" % binascii.b2a_hex(result[-2:])
|
||||
|
||||
card_challenge = result.data[12:20]
|
||||
|
@ -143,7 +143,7 @@ class Cyberflex_Card(Java_Card):
|
|||
result = self.send_apdu(apdu)
|
||||
self.secure_channel_state = security_level
|
||||
|
||||
if result.sw != self.SW_OK:
|
||||
if not self.check_sw(result.sw):
|
||||
self.secure_channel_state = SECURE_CHANNEL_NONE
|
||||
raise Exception, "Statusword after ExternalAuthenticate was %s. Warning: No successful ExternalAuthenticate; keyset might be locked soon" % binascii.b2a_hex(result[-2:])
|
||||
|
||||
|
@ -151,7 +151,7 @@ class Cyberflex_Card(Java_Card):
|
|||
|
||||
def select_application(self, aid):
|
||||
result = Java_Card.select_application(self, aid)
|
||||
if self.last_sw == self.SW_OK and aid[:5] != DEFAULT_CARD_MANAGER_AID[:5]:
|
||||
if self.check(self.last_sw) and aid[:5] != DEFAULT_CARD_MANAGER_AID[:5]:
|
||||
self.secure_channel_state = SECURE_CHANNEL_NONE
|
||||
return result
|
||||
|
||||
|
|
|
@ -4,12 +4,22 @@ from utils import C_APDU, R_APDU
|
|||
DEBUG = True
|
||||
#DEBUG = False
|
||||
|
||||
## Constants for check_sw()
|
||||
PURPOSE_SUCCESS = 1 # Command executed successful
|
||||
PURPOSE_RETRY = 2 # Command executed successful but needs retry with correct length
|
||||
PURPOSE_SM_OK = 3 # Command not executed successful or with warnings, but response still contains SM objects
|
||||
|
||||
class Card:
|
||||
DRIVER_NAME = "Generic"
|
||||
APDU_GET_RESPONSE = C_APDU(ins=0xc0)
|
||||
APDU_VERIFY_PIN = C_APDU(ins=0x20)
|
||||
SW_OK = '\x90\x00'
|
||||
SW1_RETRY = 0x61 ## If this SW1 is received then GET RESPONSE should be called with SW2
|
||||
PURPOSE_SUCCESS, PURPOSE_RETRY, PURPOSE_SM_OK = PURPOSE_SUCCESS, PURPOSE_RETRY, PURPOSE_SM_OK
|
||||
## Map for check_sw()
|
||||
STATUS_MAP = {
|
||||
PURPOSE_SUCCESS: ("\x90\x00", ),
|
||||
PURPOSE_RETRY: ("61??", ), ## If this is received then GET RESPONSE should be called with SW2
|
||||
PURPOSE_SM_OK: ("\x90\x00",)
|
||||
}
|
||||
## Note: an item in this list must be a tuple of (atr, mask) where atr is a binary
|
||||
## string and mask a binary mask. Alternatively mask may be None, then ATR must be a regex
|
||||
## to match on the ATRs hexlify representation
|
||||
|
@ -22,7 +32,7 @@ class Card:
|
|||
## keyword substitutions for SW1 and SW2 or a callable accepting two arguments
|
||||
## (SW1, SW2) that returns a string.
|
||||
STATUS_WORDS = {
|
||||
SW_OK: "Normal execution",
|
||||
'\x90\x00': "Normal execution",
|
||||
'61??': "%(SW2)i (0x%(SW2)02x) bytes of response data can be retrieved with GetResponse.",
|
||||
'6C??': "Bad value for LE, 0x%(SW2)02x is the correct value.",
|
||||
'63C?': lambda SW1,SW2: "The counter has reached the value '%i'" % (SW2%16)
|
||||
|
@ -62,7 +72,7 @@ class Card:
|
|||
"\xD2\x76\x00\x00\x40": ("Zentralinstitut fuer die Kassenaerztliche Versorgung in der Bundesrepublik Deutschland", ), # hpc-use-cases-01.pdf
|
||||
"\xa0\x00\x00\x02\x47": ("ICAO", ),
|
||||
}
|
||||
|
||||
|
||||
def _decode_df_name(self, value):
|
||||
result = " " + utils.hexdump(value, short=True)
|
||||
info = None
|
||||
|
@ -126,7 +136,7 @@ class Card:
|
|||
apdu = C_APDU(self.APDU_VERIFY_PIN, P2 = pin_number,
|
||||
data = pin_value)
|
||||
result = self.send_apdu(apdu)
|
||||
return result.sw == self.SW_OK
|
||||
return self.check_sw(result.sw)
|
||||
|
||||
def cmd_verify(self, pin_number, pin_value):
|
||||
"""Verify a PIN."""
|
||||
|
@ -191,7 +201,7 @@ class Card:
|
|||
def _send_with_retry(self, apdu):
|
||||
result = self._real_send(apdu)
|
||||
|
||||
if result.sw1 == self.SW1_RETRY:
|
||||
if self.check_sw(result.sw, PURPOSE_RETRY):
|
||||
## Need to call GetResponse
|
||||
gr_apdu = C_APDU(self.APDU_GET_RESPONSE, le = result.sw2) # FIXME
|
||||
result = R_APDU(self._real_send(gr_apdu))
|
||||
|
@ -217,6 +227,10 @@ class Card:
|
|||
self.last_result = result
|
||||
return result
|
||||
|
||||
def check_sw(self, sw, purpose = None):
|
||||
if purpose is None: purpose = Card.PURPOSE_SUCCESS
|
||||
return self.match_statusword(self.STATUS_MAP[purpose], sw)
|
||||
|
||||
def can_handle(cls, card):
|
||||
"""Determine whether this class can handle a given pycsc object."""
|
||||
ATR = card.status().get("ATR","")
|
||||
|
@ -235,33 +249,42 @@ class Card:
|
|||
def get_prompt(self):
|
||||
return "(%s)" % self.DRIVER_NAME
|
||||
|
||||
def match_statusword(swlist, sw):
|
||||
"""Try to find sw in swlist.
|
||||
swlist must be a list of either binary statuswords (two bytes), hexadecimal statuswords (four bytes) or fnmatch patterns on a hexadecimal statusword.
|
||||
Returns: The element that matched (either two bytes, four bytes or the fnmatch pattern)."""
|
||||
if sw in swlist:
|
||||
return sw
|
||||
sw = binascii.hexlify(sw).upper()
|
||||
if sw in swlist:
|
||||
return sw
|
||||
for value in swlist:
|
||||
if fnmatch.fnmatch(sw, value):
|
||||
return value
|
||||
return None
|
||||
match_statusword = staticmethod(match_statusword)
|
||||
|
||||
def decode_statusword(self):
|
||||
if self.last_sw is None:
|
||||
return "No command executed so far"
|
||||
else:
|
||||
retval = None
|
||||
|
||||
retval = self.STATUS_WORDS.get(self.last_sw)
|
||||
matched_sw = self.match_statusword(self.STATUS_WORDS.keys(), self.last_sw)
|
||||
if matched_sw is not None:
|
||||
retval = self.STATUS_WORDS.get(matched_sw)
|
||||
if isinstance(retval, str):
|
||||
retval = retval % { "SW1": ord(self.last_sw[0]),
|
||||
"SW2": ord(self.last_sw[1]) }
|
||||
|
||||
elif callable(retval):
|
||||
retval = retval( ord(self.last_sw[0]),
|
||||
ord(self.last_sw[1]) )
|
||||
|
||||
if retval is None:
|
||||
retval = self.STATUS_WORDS.get(binascii.hexlify(self.last_sw).upper())
|
||||
if retval is None:
|
||||
target = binascii.b2a_hex(self.last_sw).upper()
|
||||
for (key, value) in self.STATUS_WORDS.items():
|
||||
if fnmatch.fnmatch(target, key):
|
||||
if isinstance(value, str):
|
||||
retval = value % { "SW1": ord(self.last_sw[0]),
|
||||
"SW2": ord(self.last_sw[1]) }
|
||||
break
|
||||
|
||||
elif callable(value):
|
||||
retval = value( ord(self.last_sw[0]),
|
||||
ord(self.last_sw[1]) )
|
||||
break
|
||||
|
||||
if retval is None:
|
||||
return "Unknown SW (SW %s)" % binascii.b2a_hex(self.last_sw)
|
||||
else:
|
||||
return "%s (SW %s)" % (retval, binascii.b2a_hex(self.last_sw))
|
||||
return "Unknown SW (SW %s)" % binascii.b2a_hex(self.last_sw)
|
||||
else:
|
||||
return "%s (SW %s)" % (retval, binascii.b2a_hex(self.last_sw))
|
||||
|
||||
def get_protocol(self):
|
||||
return ((self.card.status()["Protocol"] == pycsc.SCARD_PROTOCOL_T0) and (0,) or (1,))[0]
|
||||
|
|
|
@ -4,7 +4,9 @@ from generic_card import *
|
|||
class GSM_Card(Card):
|
||||
DRIVER_NAME = "GSM"
|
||||
APDU_GET_RESPONSE = C_APDU("\xa0\xC0\x00\x00")
|
||||
SW1_RETRY = 0x9F
|
||||
STATUS_MAP = {
|
||||
PURPOSE_RETRY: ("9F??", )
|
||||
}
|
||||
|
||||
ATRS = [
|
||||
("3bff9500ffc00a1f438031e073f62113574a334861324147d6", None),
|
||||
|
|
|
@ -72,7 +72,7 @@ class ISO_7816_4_Card(Card):
|
|||
contents = contents + result.data
|
||||
offset = offset + len(result.data)
|
||||
|
||||
if result.sw != self.SW_OK:
|
||||
if not self.check_sw(result.sw):
|
||||
break
|
||||
else:
|
||||
had_one = True
|
||||
|
@ -123,7 +123,7 @@ class ISO_7816_4_Card(Card):
|
|||
result = self.send_apdu(
|
||||
C_APDU(self.APDU_SELECT_APPLICATION,
|
||||
data = aid, le = 0) ) ## FIXME With or without le
|
||||
if result.sw == self.SW_OK:
|
||||
if self.check_sw(result.sw):
|
||||
Application.load_applications(self, aid)
|
||||
return result
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ from generic_application import Application
|
|||
import struct, sha, binascii
|
||||
from utils import hexdump, C_APDU
|
||||
from tcos_card import SE_Config, TCOS_Security_Environment
|
||||
from generic_card import Card
|
||||
import crypto_utils, tcos_card
|
||||
|
||||
class Passport_Security_Environment(TCOS_Security_Environment):
|
||||
|
@ -37,7 +38,7 @@ class Passport_Security_Environment(TCOS_Security_Environment):
|
|||
|
||||
if apdu.case() in (2,4):
|
||||
if apdu.Le == 0:
|
||||
apdu.Le = 0xff # FIXME: Probably not the right way
|
||||
apdu.Le = 0xe7 # FIXME: Probably not the right way
|
||||
new_apdu.append("97(%02x)" % apdu.Le)
|
||||
|
||||
new_apdu.append("8E()00")
|
||||
|
@ -70,11 +71,14 @@ class Passport_Application(Application):
|
|||
DRIVER_NAME = "Passport"
|
||||
APDU_GET_RANDOM = C_APDU(CLA=0, INS=0x84, Le=0x08)
|
||||
APDU_MUTUAL_AUTHENTICATE = C_APDU(CLA=0, INS=0x82, Le=0x28)
|
||||
SW_OK = "\x90\x00"
|
||||
|
||||
AID_LIST = [
|
||||
"a0000002471001"
|
||||
]
|
||||
STATUS_MAP = {
|
||||
Card.PURPOSE_SM_OK: ("6282", "6982")
|
||||
}
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.ssc = None
|
||||
|
@ -116,7 +120,7 @@ class Passport_Application(Application):
|
|||
|
||||
print
|
||||
result = self.send_apdu(self.APDU_GET_RANDOM)
|
||||
assert result.sw == self.SW_OK
|
||||
assert self.check_sw(result.sw)
|
||||
|
||||
rnd_icc = result.data
|
||||
if verbose:
|
||||
|
@ -139,7 +143,7 @@ class Passport_Application(Application):
|
|||
print
|
||||
auth_apdu = C_APDU(self.APDU_MUTUAL_AUTHENTICATE, data = Eifd + Mifd)
|
||||
result = self.send_apdu(auth_apdu)
|
||||
assert result.sw == self.SW_OK
|
||||
assert self.check_sw(result.sw)
|
||||
|
||||
resp_data = result.data
|
||||
Eicc = resp_data[:-8]
|
||||
|
|
|
@ -8,6 +8,9 @@ class Postcard_Card(ISO_7816_4_Card):
|
|||
APDU_SELECT_FILE = C_APDU(cla=CLA,ins=0xa4)
|
||||
APDU_READ_BINARY = C_APDU(cla=CLA,ins=0xb0,le=0x80)
|
||||
|
||||
STATUS_MAP = {
|
||||
Card.PURPOSE_SUCCESS: ("90[124]0", )
|
||||
}
|
||||
|
||||
ATRS = [
|
||||
("3f65351002046c90..", None),
|
||||
|
@ -17,7 +20,7 @@ class Postcard_Card(ISO_7816_4_Card):
|
|||
def _get_binary(self, offset, length):
|
||||
command = C_APDU(self.APDU_READ_BINARY, p1 = offset >> 8, p2 = offset & 0xff, le = length)
|
||||
result = self.send_apdu(command)
|
||||
assert result.sw == self.SW_OK
|
||||
assert self.check_sw(result.sw)
|
||||
|
||||
return result.data
|
||||
|
||||
|
@ -93,6 +96,7 @@ class Postcard_Card(ISO_7816_4_Card):
|
|||
print "\t%20s '%s'" % ("", binascii.a2b_hex(value))
|
||||
|
||||
def cmd_give_pin(self):
|
||||
"Enter a pin"
|
||||
old = sys.modules["cards.generic_card"].DEBUG
|
||||
try:
|
||||
pin = getpass.getpass("Enter PIN: ")
|
||||
|
|
|
@ -77,7 +77,7 @@ class TCOS_Security_Environment(object):
|
|||
|
||||
def after_send(self, result):
|
||||
self.last_r_apdu = result
|
||||
if result.sw == self.card.SW_OK:
|
||||
if self.card.check_sw(result.sw, self.card.PURPOSE_SM_OK):
|
||||
if (self.last_c_apdu.cla & 0xf0) == 0x00:
|
||||
if self.last_c_apdu.ins == 0x22:
|
||||
self.parse_mse(self.last_c_apdu)
|
||||
|
|
|
@ -80,10 +80,10 @@ class Cyberflex_Shell(Shell):
|
|||
|
||||
self.parse_and_execute(line)
|
||||
|
||||
if self.card.sw_changed and self.card.last_sw != self.card.SW_OK \
|
||||
if self.card.sw_changed and not self.card.check_sw(self.card.last_sw) \
|
||||
and self.card.last_sw not in ignored_SWs:
|
||||
|
||||
print "SW was not %s. Ignore (i) or Abort (a)? " % binascii.hexlify(self.card.SW_OK),
|
||||
print "SW was not OK. Ignore (i) or Abort (a)? ",
|
||||
answer = sys.stdin.readline()
|
||||
if answer[0].lower() in ('i', "\n"):
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue