diff --git a/crypto_utils.py b/crypto_utils.py new file mode 100644 index 0000000..0004ee9 --- /dev/null +++ b/crypto_utils.py @@ -0,0 +1,71 @@ +import sys, binascii, utils, random +from Crypto.Cipher import DES3 + +iv = '\x00' * 8 +PADDING = '\x80' + '\x00' * 7 + +def verify_card_cryptogram(session_key, host_challenge, + card_challenge, card_cryptogram): + message = host_challenge + card_challenge + expected = calculate_MAC(session_key, message, iv) + + print >>sys.stderr, "Original: %s" % binascii.b2a_hex(card_cryptogram) + print >>sys.stderr, "Expected: %s" % binascii.b2a_hex(expected) + + return card_cryptogram == expected + +def calculate_host_cryptogram(session_key, card_challenge, + host_challenge): + message = card_challenge + host_challenge + return calculate_MAC(session_key, message, iv) + +def calculate_MAC(session_key, message, iv): + print >>sys.stderr, "Doing MAC for: %s" % utils.hexdump(message, indent = 17) + + cipher = DES3.new(session_key, DES3.MODE_CBC, iv) + block_count = len(message) / cipher.block_size + for i in range(block_count): + cipher.encrypt(message[i*cipher.block_size:(i+1)*cipher.block_size]) + + last_block_length = len(message) % cipher.block_size + last_block = (message[len(message)-last_block_length:]+PADDING)[:cipher.block_size] + + return cipher.encrypt( last_block ) + +def get_derivation_data(host_challenge, card_challenge): + return card_challenge[4:8] + host_challenge[:4] + \ + card_challenge[:4] + host_challenge[4:8] + +def get_session_key(auth_key, host_challenge, card_challenge): + cipher = DES3.new(auth_key, DES3.MODE_ECB) + return cipher.encrypt(get_derivation_data(host_challenge, card_challenge)) + +def generate_host_challenge(): + random.seed() + return "".join([chr(random.randint(0,255)) for e in range(8)]) + +def andstring(string1, string2): + if len(string1) != len(string2): + raise ValueError, "string1 and string2 must be of equal length" + result = [] + for i in range(len(string1)): + result.append( chr(ord(string1[i]) & ord(string2[i])) ) + return "".join(result) + +if __name__ == "__main__": + default_key = binascii.a2b_hex("404142434445464748494A4B4C4D4E4F") + + host_chal = binascii.a2b_hex("".join("89 45 19 BF BC 1A 5B D8".split())) + card_chal = binascii.a2b_hex("".join("27 4D B7 EA CA 66 CE 44".split())) + card_crypto = binascii.a2b_hex("".join("8A D4 A9 2D 9B 6B 24 E0".split())) + + session_key = get_session_key(default_key, host_chal, card_chal) + print "Session-Key: ", utils.hexdump(session_key) + + print verify_card_cryptogram(session_key, host_chal, card_chal, card_crypto) + + host_crypto = calculate_host_cryptogram(session_key, card_chal, host_chal) + print "Host-Crypto: ", utils.hexdump( host_crypto ) + + external_authenticate = binascii.a2b_hex("".join("84 82 01 00 10".split())) + host_crypto + print utils.hexdump(calculate_MAC(session_key, external_authenticate, iv)) diff --git a/cyberflex-shell.e3p b/cyberflex-shell.e3p index 04469af..33da624 100644 --- a/cyberflex-shell.e3p +++ b/cyberflex-shell.e3p @@ -1,7 +1,7 @@ - + @@ -12,6 +12,28 @@ utils.py + + crypto_utils.py + + + cyberflex-shell.py + + + cards + cyberflex_card.py + + + cards + generic_card.py + + + cards + java_card.py + + + cards + __init__.py + diff --git a/cyberflex-shell.py b/cyberflex-shell.py new file mode 100755 index 0000000..e5b7cd2 --- /dev/null +++ b/cyberflex-shell.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-1 -*- + +import pycsc, utils, cards, os, readline, re, binascii, sys + +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") + + +readerName = pycsc.listReader()[0] +newState = pycsc.getStatusChange(ReaderStates=[{'Reader': readerName, 'CurrentState':pycsc.SCARD_STATE_UNAWARE}]) + +print "Cyberflex shell" +print "Using reader: %s" % readerName +print "Card present: %s" % ((newState[0]['EventState'] & pycsc.SCARD_STATE_PRESENT) and "yes" or "no") + +if not newState[0]['EventState'] & pycsc.SCARD_STATE_PRESENT: + print "Please insert card ..." + + while not newState[0]['EventState'] & pycsc.SCARD_STATE_PRESENT \ + or newState[0]['EventState'] & pycsc.SCARD_STATE_MUTE: + + newState = pycsc.getStatusChange(ReaderStates=[{'Reader': readerName, 'CurrentState':newState[0]['EventState']}]) + + if newState[0]['EventState'] & pycsc.SCARD_STATE_MUTE: + print "Card is mute, please retry ..." + + print "Card present: %s" % ((newState[0]['EventState'] & pycsc.SCARD_STATE_PRESENT) and "yes" or "no") + +print "ATR: %s" % utils.hexdump(newState[0]['Atr'], short = True) +card_class = cards.find_class(newState[0]['Atr']) + +card = card_class() + +line = "" +apduregex = re.compile(r'^\s*([0-9a-f]{2}\s*){4,}$', re.I) +while line != "exit": + try: + line = raw_input("%s > " % card.get_prompt()) + except EOFError: + print + break + + if line.strip() == "": + continue + + 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] + print "%s: %s" % (exctype, value) + + 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)