From f01836762b4ad3f3b90785c5aadf69e6ea972305 Mon Sep 17 00:00:00 2001 From: hploetz Date: Mon, 12 Feb 2007 20:09:48 +0000 Subject: [PATCH] TCOS_Security_Environment: Handle 0x98/0x99 SM object Passport_Application: Implement secure messaging git-svn-id: svn+ssh://localhost/home/henryk/svn/cyberflex-shell/trunk@176 f711b948-2313-0410-aaa9-d29f33439f0b --- cards/passport_application.py | 94 +++++++++++++++++++++++++++++++++-- cards/tcos_card.py | 14 ++++-- 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/cards/passport_application.py b/cards/passport_application.py index eed43f8..202c2af 100644 --- a/cards/passport_application.py +++ b/cards/passport_application.py @@ -1,7 +1,70 @@ from generic_application import Application import struct, sha, binascii from utils import hexdump, C_APDU -import crypto_utils +from tcos_card import SE_Config, TCOS_Security_Environment +import crypto_utils, tcos_card + +class Passport_Security_Environment(TCOS_Security_Environment): + def __init__(self, card): + TCOS_Security_Environment.__init__(self, card) + self.last_vanilla_c_apdu = None + + # Set up a fake SE config to be able to reuse the TCOS code + self.set_key( 1, self.card.KSenc) + enc_config = "\x80\x01\x0d\x83\x01\x01\x85\x00" + self.set_key( 2, self.card.KSmac) + mac_config = "\x80\x01\x0d\x83\x01\x02\x85\x00" + + self.set_config( tcos_card.SE_APDU, tcos_card.TEMPLATE_CCT, SE_Config(mac_config) ) + self.set_config( tcos_card.SE_RAPDU, tcos_card.TEMPLATE_CCT, SE_Config(mac_config) ) + + self.set_config( tcos_card.SE_APDU, tcos_card.TEMPLATE_CT, SE_Config(enc_config) ) + self.set_config( tcos_card.SE_RAPDU, tcos_card.TEMPLATE_CT, SE_Config(enc_config) ) + + def before_send(self, apdu): + self.last_vanilla_c_apdu = C_APDU(apdu) + if (apdu.cla & 0x80 != 0x80) and (apdu.CLA & 0x0C != 0x0C): + # Transform for SM + apdu.CLA = apdu.CLA | 0x0C + apdu_string = binascii.b2a_hex(apdu.render()) + new_apdu = [apdu_string[:8]] + new_apdu.append("YY") + + if apdu.case() in (3,4): + new_apdu.append("87[01") + new_apdu.append(binascii.b2a_hex(apdu.data)) + new_apdu.append("]") + + if apdu.case() in (2,4): + if apdu.Le == 0: + apdu.Le = 0xff # FIXME: Probably not the right way + new_apdu.append("97(%02x)" % apdu.Le) + + new_apdu.append("8E()00") + + new_apdu_string = "".join(new_apdu) + apdu = C_APDU.parse_fancy_apdu(new_apdu_string) + + return TCOS_Security_Environment.before_send(self, apdu) + + def after_send(self, result): + if (self.last_vanilla_c_apdu.cla & 0x80 != 0x80) and (self.last_vanilla_c_apdu.CLA & 0x0C != 0x0C): + # Inject fake response descriptor so that TCOS_Security_Environment.after_send sees the need to authenticate/decrypt + response_descriptor = "\x99\x00\x8e\x00" + if self.last_vanilla_c_apdu.case() in (2,4): + response_descriptor = "\x87\x00" + response_descriptor + response_descriptor = "\xba" + chr(len(response_descriptor)) + response_descriptor + + self.last_c_apdu.data = self.last_c_apdu.data + response_descriptor + + return TCOS_Security_Environment.after_send(self, result) + + + def _mac(self, data): + (ssc,) = struct.unpack(">Q", self.card.ssc) + ssc = ssc + 1 + self.card.ssc = struct.pack(">Q", ssc) + return Passport_Application._mac(self.card.KSmac, data, self.card.ssc, dopad=False) class Passport_Application(Application): DRIVER_NAME = "Passport" @@ -17,6 +80,7 @@ class Passport_Application(Application): self.ssc = None self.KSenc = None self.KSmac = None + self.se = None def derive_key(Kseed, c): """Derive a key according to TR-PKI mrtds ICC read-only access v1.1 annex E.1. @@ -106,17 +170,35 @@ class Passport_Application(Application): self.KSmac = self.derive_key(KSseed, 2) self.ssc = rnd_icc[-4:] + rnd_ifd[-4:] + if False: + self.KSenc = binascii.a2b_hex("979EC13B1CBFE9DCD01AB0FED307EAE5") + self.KSmac = binascii.a2b_hex("F1CB1F1FB5ADF208806B89DC579DC1F8") + self.ssc = binascii.a2b_hex("887022120C06C226") + if verbose: print "KSseed = %s" % hexdump(KSseed) print "KSenc = %s" % hexdump(self.KSenc) print "KSmac = %s" % hexdump(self.KSmac) print "ssc = %s" % hexdump(self.ssc) + + self.se = Passport_Security_Environment(self) - def _mac(key, data, ssc = None): + def before_send(self, apdu): + if self.se: + apdu = self.se.before_send(apdu) + return apdu + + def after_send(self, result): + if self.se: + result = self.se.after_send(result) + return result + + def _mac(key, data, ssc = None, dopad=True): if ssc: data = ssc + data - topad = 8 - len(data) % 8 - data = data + "\x80" + ("\x00" * (topad-1)) + if dopad: + topad = 8 - len(data) % 8 + data = data + "\x80" + ("\x00" * (topad-1)) a = crypto_utils.cipher(True, "des-cbc", key[:8], data) b = crypto_utils.cipher(False, "des-ecb", key[8:16], a[-8:]) c = crypto_utils.cipher(True, "des-ecb", key[:8], b) @@ -133,6 +215,10 @@ class Passport_Application(Application): return r _make_random = staticmethod(_make_random) + def get_prompt(self): + return "(%s)%s" % (self.DRIVER_NAME, self.se and "[SM]" or "") + + COMMANDS = { "perform_bac": cmd_perform_bac, } diff --git a/cards/tcos_card.py b/cards/tcos_card.py index 1c49c91..f5c24a9 100644 --- a/cards/tcos_card.py +++ b/cards/tcos_card.py @@ -126,8 +126,8 @@ class TCOS_Security_Environment(object): tlv_data = self.decrypt_response(tlv_data) #data = TLV_utils.pack(tlv_data, recalculate_length = True) - data = self.deformat_response(tlv_data) - new_apdu = R_APDU(rapdu, data = data) + data, sw = self.deformat_response(tlv_data, rapdu.sw) + new_apdu = R_APDU(rapdu, data = data, sw = sw) result = new_apdu except ValueError: @@ -137,8 +137,8 @@ class TCOS_Security_Environment(object): return result - def deformat_response(self, tlv_data): - WHITELIST = (0x84, 0x86) + def deformat_response(self, tlv_data, sw): + WHITELIST = (0x84, 0x86, 0x98) result = [] is_ok = True @@ -154,6 +154,10 @@ class TCOS_Security_Environment(object): if t in WHITELIST: if t == 0x86: result.append( value[1:] ) + elif t == 0x98: + if sw != value: + print "Warning: SW from SM not equal SW from RAPDU" + sw = value else: result.append( value ) else: @@ -161,7 +165,7 @@ class TCOS_Security_Environment(object): else: result.append( TLV_utils.pack( tlv_data, recalculate_length = True) ) - return "".join(result) + return "".join(result), sw def encrypt_command(self, tlv_data): config = self.get_config(SE_APDU, TEMPLATE_CT)