From bcd0155238037b3ce8e8a951cd17ebef19ef5b63 Mon Sep 17 00:00:00 2001 From: frankmorgner Date: Fri, 16 Dec 2011 19:22:29 +0000 Subject: [PATCH] added PACE support for nPA emulator git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@671 96b47cad-a561-4643-ad3b-153ac7d7599c --- .../vpicc/virtualsmartcard/CardGenerator.py | 3 +- .../virtualsmartcard/VirtualSmartcard.py | 10 +- .../src/vpicc/virtualsmartcard/cards/nPA.py | 143 +++++++++++++++++- 3 files changed, 147 insertions(+), 9 deletions(-) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py index a966dee..534c376 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py @@ -148,7 +148,8 @@ class CardGenerator(object): ALGO_MAPPING["\x04\x00\x7f\x00\x07\x02\x02\x04\x02\x02"] = "PACE" self.mf = MF() self.mf.append(TransparentStructureEF(parent=self.mf, fid=0x011c, shortfid=0x1c, data=card_access)) - self.sam = nPA_SAM(self.mf) + self.sam = nPA_SAM(pin="pin", can="can", mrz="mrz", puk="puk", mf=self.mf) + self.sam.card_access = card_access def __generate_cryptoflex(self): """Generate the Filesystem and SAM of a cryptoflex card""" diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py index 9d3ec76..564608f 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py @@ -334,7 +334,8 @@ class Iso7816OS(SmartcardOS): answer = self.formatResult(Iso7816OS.seekable(c.ins), c.effective_Le, result, sw, False) except SwError as e: logging.info(e.message) - #traceback.print_exception(*sys.exc_info()) + import traceback + traceback.print_exception(*sys.exc_info()) sw = e.sw result = "" answer = self.formatResult(False, 0, result, sw, False) @@ -495,12 +496,16 @@ class RelayOS(SmartcardOS): class NPAOS(Iso7816OS): + def __init__(self, mf, sam, ins2handler=None, maxle=MAX_EXTENDED_LE): + Iso7816OS.__init__(self, mf, sam, ins2handler, maxle) + self.ins2handler[0x86] = self.SAM.general_authenticate + def formatResult(self, seekable, le, data, sw, sm): if seekable: # when le = 0 then we want to have 0x9000. here we only have the # effective le, which is either MAX_EXTENDED_LE or MAX_SHORT_LE, # depending on the APDU. Note that the following distinguisher has - # one false positives + # one false positive if le > len(data) and le != MAX_EXTENDED_LE and le != MAX_SHORT_LE: sw = SW["WARN_EOFBEFORENEREAD"] @@ -513,6 +518,7 @@ class NPAOS(Iso7816OS): # sizeof(int) taken from asizof-package {{{ _Csizeof_short = len(struct.pack('h', 0)) +# }}} VPCD_CTRL_LEN = 1 diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py index 124402c..7ed0339 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/nPA.py @@ -21,6 +21,9 @@ from virtualsmartcard.SEutils import ControlReferenceTemplate from virtualsmartcard.SWutils import SwError, SW from virtualsmartcard.ConstantDefinitions import CRT_TEMPLATE from virtualsmartcard.TLVutils import unpack, bertlv_pack +from virtualsmartcard.SmartcardFilesystem import make_property +from chat import CHAT +import pace class nPA_AT_CRT(ControlReferenceTemplate): def __init__(self): @@ -50,11 +53,139 @@ class nPA_AT_CRT(ControlReferenceTemplate): return 0x9000 , "" -class nPA_SAM(SAM): - """ - SAM for ICAO ePassport. Implements Basic access control and key derivation - for Secure Messaging. - """ - def __init__(self, mf): +class nPA_SAM(SAM): + # TODO move EAC to the securiity environment + # TODO call __eac_abort whenever an error occurred + + eac_step = make_property("eac_step", "next step to performed for EAC") + + def __init__(self, pin, can, mrz, puk, mf): SAM.__init__(self, None, None, mf) self.current_SE.at = nPA_AT_CRT() + self.eac_step = 0 + self.eac_ctx = None + self.sec = None + self.step2handler = { + 0: self.__eac_pace_step1, + 1: self.__eac_pace_step2, + 2: self.__eac_pace_step3, + 3: self.__eac_pace_step4, + } + self.pin = pin + self.can = can + self.mrz = mrz + self.puk = puk + + def general_authenticate(self, p1, p2, data): + def notImplemented(*argz, **args): + raise SwError(SW["WARN_NOINFO63"]) + + if (p1, p2) != (0x00, 0x00): + raise SwError(SW["ERR_INCORRECTPARAMETERS"]) + + return self.step2handler.get(self.eac_step, notImplemented)(data) + + def __eac_abort(self): + pace.EAC_CTX_clear_free(self.eac_ctx) + pace.PACE_SEC_clear_free(self.sec) + + @staticmethod + def __unpack_general_authenticate(data): + data_structure = [] + structure = unpack(data) + for tlv in structure: + tag, length, value = tlv + if tag == 0x7c: + data_structure = value + else: + raise SwError(SW["ERR_INCORRECTPARAMETERS"]) + return data_structure + + @staticmethod + def __pack_general_authenticate(data): + tlv_data = bertlv_pack(data) + return bertlv_pack([[0x7c, len(tlv_data), tlv_data]]) + + def __eac_pace_step1(self, data): + tlv_data = nPA_SAM.__unpack_general_authenticate(data) + if self.current_SE.at.algorithm != "PACE" or tlv_data != []: + print 'value %r' % tlv_data + raise SwError(SW["WARN_NOINFO63"]) + + self.__eac_abort() + + self.eac_ctx = pace.EAC_CTX_new() + if self.current_SE.at.keyref == '\x01': + self.sec = pace.PACE_SEC_new(self.mrz, pace.PACE_MRZ) + elif self.current_SE.at.keyref == '\x02': + self.sec = pace.PACE_SEC_new(self.can, pace.PACE_CAN) + elif self.current_SE.at.keyref == '\x03': + self.sec = pace.PACE_SEC_new(self.pin, pace.PACE_PIN) + elif self.current_SE.at.keyref == '\x04': + self.sec = pace.PACE_SEC_new(self.puk, pace.PACE_PUK) + else: + raise SwError(SW["ERR_INCORRECTPARAMETERS"]) + + ef_card_access = self.mf.select('fid', 0x011c) + ef_card_access_data = ef_card_access.getenc('data') + pace.EAC_CTX_init_ef_cardaccess(ef_card_access_data, self.eac_ctx) + + nonce = pace.buf2string(pace.PACE_STEP1_enc_nonce(self.eac_ctx, self.sec)) + resp = nPA_SAM.__pack_general_authenticate([[0x80, len(nonce), nonce]]) + + self.eac_step += 1 + + return 0x9000, resp + + def __eac_pace_step2(self, data): + tlv_data = nPA_SAM.__unpack_general_authenticate(data) + if self.current_SE.at.algorithm != "PACE": + raise SwError(SW["WARN_NOINFO63"]) + + pubkey = pace.buf2string(pace.PACE_STEP3A_generate_mapping_data(self.eac_ctx)) + + for tag, length, value in tlv_data: + if tag == 0x81: + pace.PACE_STEP3A_map_generator(self.eac_ctx, pace.get_buf(value)) + else: + raise SwError(SW["ERR_INCORRECTPARAMETERS"]) + + self.eac_step += 1 + + return 0x9000, nPA_SAM.__pack_general_authenticate([[0x82, len(pubkey), pubkey]]) + + def __eac_pace_step3(self, data): + tlv_data = nPA_SAM.__unpack_general_authenticate(data) + if self.current_SE.at.algorithm != "PACE": + raise SwError(SW["WARN_NOINFO63"]) + + my_epp_pubkey = pace.buf2string(pace.PACE_STEP3B_generate_ephemeral_key(self.eac_ctx)) + + for tag, length, value in tlv_data: + if tag == 0x83: + self.pace_opp_pub_key = pace.get_buf(value) + pace.PACE_STEP3B_compute_shared_secret(self.eac_ctx, self.pace_opp_pub_key) + else: + raise SwError(SW["ERR_INCORRECTPARAMETERS"]) + + self.eac_step += 1 + + return 0x9000, nPA_SAM.__pack_general_authenticate([[0x84, len(my_epp_pubkey), my_epp_pubkey]]) + + def __eac_pace_step4(self, data): + tlv_data = nPA_SAM.__unpack_general_authenticate(data) + if self.current_SE.at.algorithm != "PACE": + raise SwError(SW["WARN_NOINFO63"]) + + pace.PACE_STEP3C_derive_keys(self.eac_ctx) + my_token = pace.buf2string(pace.PACE_STEP3D_compute_authentication_token(self.eac_ctx, self.pace_opp_pub_key)) + + for tag, length, value in tlv_data: + if tag == 0x85: + pace.PACE_STEP3D_verify_authentication_token(self.eac_ctx, pace.get_buf(value)) + else: + raise SwError(SW["ERR_INCORRECTPARAMETERS"]) + + self.eac_step += 1 + + return 0x9000, nPA_SAM.__pack_general_authenticate([[0x86, len(my_token), my_token]])