diff --git a/cards/cyberflex_card.py b/cards/cyberflex_card.py index 9d695cc..35329a7 100644 --- a/cards/cyberflex_card.py +++ b/cards/cyberflex_card.py @@ -188,39 +188,36 @@ class Cyberflex_Card(Java_Card): return result[0] == 0x0 - def cmd_delete(self, *args): - if len(args) != 1: - raise TypeError, "Must have exactly one argument." - aid = binascii.a2b_hex("".join(args[0].split())) + def cmd_delete(self, aid): + """Delete the object identified by aid.""" + aid = binascii.a2b_hex("".join(aid.split())) self.delete(aid) - def cmd_status(self, *args): - if len(args) > 1: - raise TypeError, "Can have at most one argument." - if len(args) == 1: - reference_control = int(args[0], 0) - else: - reference_control = 0x20 + def cmd_status(self, reference_control = "0x20"): + """Execute a GetStatus command and return the result.""" + reference_control = int(reference_control, 0) result = self.get_status(reference_control) utils.parse_status(result[:-2]) - def cmd_secure(self, *args): - if len(args) == 0: + def cmd_secure(self, keyset_version=None, key_index=None, security_level=None): + """Open a secure channel. + If given, keyset_version and key_index must be integers while security_level can be one of 0, clear, 1, mac, 3, macenc, mac+enc.""" + if keyset_version is None and key_index is None and security_level is None: arg1 = 0 arg2 = 0 arg3int = SECURE_CHANNEL_MAC - elif len(args)== 3: - arg1 = int(args[0],0) - arg2 = int(args[1],0) + elif keyset_version is not None and key_index is not None and security_level is not None: + arg1 = int(keyset_version,0) + arg2 = int(key_index,0) if arg1 not in range(256): raise ValueError, "keyset_version must be between 0 and 255 (inclusive)." if arg2 not in (0,1): raise ValueError, "key_index must be 0 or 1." - arg3 = args[2].strip().lower() + arg3 = security_level.strip().lower() try: - arg3int = int(args[2],0) + arg3int = int(security_level,0) except: arg3int = None @@ -234,19 +231,19 @@ class Cyberflex_Card(Java_Card): raise TypeError, "Must give none or three arguments." self.open_secure_channel(arg1, arg2, arg3int) - def cmd_setkey(self, *args): - if len(args) != 2: - raise TypeError, "Need exactly two arguments: keyset index and key" - arg1 = args[0].strip().lower() + def cmd_setkey(self, key_index, key): + """Set a key in the current keyset. + key_index should be one of 0, all, 1, enc, auth, 2, mac, 3, kek.""" + arg1 = key_index.strip().lower() try: arg1int = int(arg1,0) except: arg1int = None - if len(args[1]) != 16: - arg2 = binascii.a2b_hex("".join(args[1].split())) + if len(key) != 16: + arg2 = binascii.a2b_hex("".join(key.split())) else: - arg2 = args[1] + arg2 = key if len(arg2) != 16: raise TypeError, "Need either exactly 16 binary bytes or 16 hexadezimal bytes for the key argument." @@ -263,27 +260,27 @@ class Cyberflex_Card(Java_Card): if all or arg1int == KEY_KEK or arg1 == "kek": self.keyset[KEY_KEK] = arg2 - def cmd_printkeyset(self, *args): + def cmd_printkeyset(self): + """Print the current keyset.""" print "ENC,AUTH:", utils.hexdump(self.keyset[KEY_AUTH], short=True) print "MAC: ", utils.hexdump(self.keyset[KEY_MAC], short=True) print "KEK: ", utils.hexdump(self.keyset[KEY_KEK], short=True) - def cmd_resetkeyset(self, *args): + def cmd_resetkeyset(self): + """Reset the keyset to the default keyset for this card.""" self.keyset = dict(DEFAULT_KEYSET) - def cmd_savekeyset(self, *args): - if len(args) != 1: - raise TypeError, "Must give exactly one argument: the name of the file to save the keyset in." - fd = file(args[0], "w") + def cmd_savekeyset(self, filename): + """Saves the keyset to the named file.""" + fd = file(filename, "w") fd.write(self.keyset[KEY_AUTH]) fd.write(self.keyset[KEY_MAC]) fd.write(self.keyset[KEY_KEK]) fd.close() - def cmd_loadkeyset(self, *args): - if len(args) != 1: - raise TypeError, "Must give exactly one argument: the name of the file to load the keyset from." - fd = file(args[0], "r") + def cmd_loadkeyset(self, filename): + """Loads the keyset from the named file.""" + fd = file(filename, "r") keys = fd.read(16*3) if len(keys) != 16*3: del keys @@ -311,22 +308,14 @@ class Cyberflex_Card(Java_Card): COMMANDS = dict(Java_Card.COMMANDS) COMMANDS.update( { - "status": (cmd_status, "status [reference_control]", - """Execute a GetStatus command and return the result."""), - "open_secure_channel": (cmd_secure, "open_secure_channel [keyset_version key_index security_level]", - """Open a secure channel. If given, keyset_version and key_index must be integers while security_level can be one of 0, clear, 1, mac, 3, macenc, mac+enc."""), - "set_key": (cmd_setkey, "set_key key_index key", - """Set a key in the current keyset. key_index should be one of 0, all, 1, enc, auth, 2, mac, 3, kek."""), - "print_keyset": (cmd_printkeyset, "print_keyset", - """Print the current keyset."""), - "reset_keyset": (cmd_resetkeyset, "reset_keyset", - """Reset the keyset to the default keyset for this card."""), - "save_keyset": (cmd_savekeyset, "save_keyset filename", - """Saves the keyset to the named file."""), - "load_keyset": (cmd_loadkeyset, "load_keyset filename", - """Loads the keyset from the named file."""), - "delete": (cmd_delete, "delete aid", - """Delete the object identified by aid.""") + "status": cmd_status, + "open_secure_channel": cmd_secure, + "set_key": cmd_setkey, + "print_keyset": cmd_printkeyset, + "reset_keyset": cmd_resetkeyset, + "save_keyset": cmd_savekeyset, + "load_keyset": cmd_loadkeyset, + "delete": cmd_delete } ) STATUS_WORDS = dict(Java_Card.STATUS_WORDS) STATUS_WORDS.update( { diff --git a/cards/generic_card.py b/cards/generic_card.py index 131ac39..6d1ea10 100644 --- a/cards/generic_card.py +++ b/cards/generic_card.py @@ -41,21 +41,19 @@ class Card: result = self.send_apdu(apdu) return result == self.SW_OK - def cmd_verify(self, *args): - if len(args) != 2: - raise TypeError, "Must give exactly two arguments: pin number and pin" - pin_number = int(args[0], 0) - pin_value = binascii.a2b_hex("".join(args[1].split())) + def cmd_verify(self, pin_number, pin_value): + """Verify a PIN.""" + pin_number = int(pin_number, 0) + pin_value = binascii.a2b_hex("".join(pin_value.split())) self.verify_pin(pin_number, pin_value) - def cmd_reset(self, *args): + def cmd_reset(self): + """Reset the card.""" self.card.reconnect(init=pycsc.SCARD_RESET_CARD) COMMANDS = { - "reset": (cmd_reset, "reset", - """Reset the card."""), - "verify": (cmd_verify, "verify pin_number pin_value", - """Verify a PIN.""") + "reset": cmd_reset, + "verify": cmd_verify } def _check_apdu(apdu): diff --git a/cards/java_card.py b/cards/java_card.py index 584b662..2c3da85 100644 --- a/cards/java_card.py +++ b/cards/java_card.py @@ -18,19 +18,19 @@ class Java_Card(Card): content = aid) ) return result - def cmd_selectapplication(self, *args): - if len(args) != 1: - raise TypeError, "Must give exactly one argument: the application to select." - if self.APPLICATIONS.has_key(args[0]): - aid = self.APPLICATIONS[args[0]] + def cmd_selectapplication(self, application): + """Select an application on the card. + application can be given either as hexadezimal aid or by symbolic name (if known).""" + + if self.APPLICATIONS.has_key(application): + aid = self.APPLICATIONS[application] else: - aid = binascii.a2b_hex("".join(args[0].split())) + aid = binascii.a2b_hex("".join(application.split())) result = self.select_application(aid) if len(result) > 2: print utils.hexdump(result[:-2]) COMMANDS = dict(Card.COMMANDS) COMMANDS.update( { - "select_application": (cmd_selectapplication, "select_application application", - """Select an application on the card. application can be given either as hexadezimal aid or by symbolic name (if known).""") + "select_application": cmd_selectapplication } ) diff --git a/cyberflex-shell.py b/cyberflex-shell.py index 450deb0..3b80391 100755 --- a/cyberflex-shell.py +++ b/cyberflex-shell.py @@ -2,74 +2,15 @@ # -*- coding: iso-8859-1 -*- import pycsc, utils, cards, os, re, binascii, sys, exceptions, traceback +from shell import Shell print_backtrace = True -try: - import readline -except ImportError: - print "No readline available" - -if sys.modules.has_key("readline"): - histfile = os.path.join(os.environ["HOME"], ".cyberflex-shell.history") - try: - readline.read_history_file(histfile) - except IOError: - pass - import atexit - atexit.register(readline.write_history_file, histfile) - del os, histfile - - readline.parse_and_bind("tab: complete") - -class Cyberflex_Shell_Completer: - def __init__(self, *commands): - self.commands = commands - self.card = None - def set_card(self, card): - self.card = card - def complete(self, text, state): - found = -1 - def check(text, state, cmd, found): - if text == cmd[:len(text)]: - found = found+1 - if found == state: - return (found, cmd) - else: - return (found, None) - - if self.card is not None: - for (cmd, cmdspec) in self.card.COMMANDS.items(): - (found, retval) = check(text, state, cmd, found) - if retval is not None: - return retval - for cmdset in self.commands: - for (cmd, cmdspec) in cmdset.items(): - (found, retval) = check(text, state, cmd, found) - if retval is not None: - return retval - - return False - -def cmd_exit(card, *args): - sys.exit() -def cmd_help(card, *args): - print "Cyberflex-shell help" - print "\n%s Card commands:" % card.DRIVER_NAME - for (cmd, cmdspec) in card.COMMANDS.items(): - print "%s\n\t\t%s" % (cmdspec[1], cmdspec[2]) - print "\nShell commands:" - for (cmd, cmdspec) in COMMANDS.items(): - print "%s\n\t\t%s" % (cmdspec[1], cmdspec[2]) def cmd_atr(card, *args): + """Print the ATR of the currently inserted card.""" print "ATR: %s" % utils.hexdump(card.card.status()['ATR'], short=True) COMMANDS = { - "exit": (cmd_exit, "exit", - """Exit the shell."""), - "help": (cmd_help, "help", - """Print this help."""), - "atr": (cmd_atr, "atr", - """Print the ATR of the currently inserted card.""") + "atr": cmd_atr } if __name__ == "__main__": @@ -98,68 +39,34 @@ if __name__ == "__main__": card_class = cards.find_class(newState[0]['Atr']) card = card_class() + shell = Shell("cyberflex-shell") + shell.register_commands(card, COMMANDS) + shell.register_commands(card) - if sys.modules.has_key("readline"): - completer = Cyberflex_Shell_Completer(COMMANDS) - completer.set_card(card) - readline.set_completer(completer.complete) - + def _update_prompt(): + shell.set_prompt(card.get_prompt() + " ") + shell.register_pre_hook(_update_prompt) - line = "" - apduregex = re.compile(r'^\s*([0-9a-f]{2}\s*){4,}$', re.I) - while True: - try: - line = raw_input("%s > " % card.get_prompt()) - except EOFError: - print - break - - line = line.strip() - if line == "": - continue - + def _clear_sw(): card.sw_changed = False + shell.register_pre_hook(_clear_sw) + + apduregex = re.compile(r'^\s*([0-9a-f]{2}\s*){4,}$', re.I) + def do_raw_apdu(*args): + apdu_string = "".join(args) + if not apduregex.match(apdu_string): + raise NotImplementedError - parts = line.split() - cmd = parts[0] - if card.COMMANDS.has_key(cmd.lower()): - cmdspec = card.COMMANDS[cmd.lower()] - try: - cmdspec[0](card, *parts[1:]) - except Exception: - exctype, value = sys.exc_info()[:2] - if exctype == exceptions.SystemExit: - raise exctype, value - print "%s: %s" % (exctype, value) - if print_backtrace: - traceback.print_tb(sys.exc_info()[2]) - - elif COMMANDS.has_key(cmd.lower()): - cmdspec = COMMANDS[cmd.lower()] - try: - cmdspec[0](card, *parts[1:]) - except Exception: - exctype, value = sys.exc_info()[:2] - if exctype == exceptions.SystemExit: - raise exctype, value - print "%s: %s" % (exctype, value) - if print_backtrace: - traceback.print_tb(sys.exc_info()[2]) - - elif apduregex.match(line): - ## Might be an APDU - apdu = binascii.a2b_hex("".join(line.split())) - try: - response = card.send_apdu(apdu) - print utils.hexdump(response) - except Exception: - exctype, value = sys.exc_info()[:2] - print "%s: %s" % (exctype, value) - if print_backtrace: - traceback.print_tb(sys.exc_info()[2]) - - else: - print "Unknown command" + apdu_binary = binascii.a2b_hex("".join(apdu_string.split())) + apdu = utils.APDU(apdu_binary) + response = card.send_apdu(apdu) + print utils.hexdump(response) + shell.fallback = do_raw_apdu + + def _print_sw(): if card.sw_changed: print card.decode_statusword() + shell.register_post_hook(_print_sw) + + shell.run()