From 3277da782450f4dbb877ba34eea14a4fae8d0bc3 Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Mon, 18 May 2015 17:52:36 +0200 Subject: [PATCH] moved RelayOS into separate file --- virtualsmartcard/src/vpicc/Makefile.am | 1 + .../virtualsmartcard/VirtualSmartcard.py | 108 +-------------- .../src/vpicc/virtualsmartcard/cards/Relay.py | 126 ++++++++++++++++++ 3 files changed, 129 insertions(+), 106 deletions(-) create mode 100644 virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py diff --git a/virtualsmartcard/src/vpicc/Makefile.am b/virtualsmartcard/src/vpicc/Makefile.am index de648a0..b5e2c95 100644 --- a/virtualsmartcard/src/vpicc/Makefile.am +++ b/virtualsmartcard/src/vpicc/Makefile.am @@ -23,6 +23,7 @@ vpicc_PYTHON = virtualsmartcard/CardGenerator.py \ vpicccards_PYTHON = virtualsmartcard/cards/__init__.py \ virtualsmartcard/cards/ePass.py \ virtualsmartcard/cards/nPA.py \ + virtualsmartcard/cards/Relay.py \ virtualsmartcard/cards/cryptoflex.py do_subst = $(SED) \ diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py index 96ef7cb..d325d5f 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py @@ -23,7 +23,7 @@ from virtualsmartcard.SmartcardFilesystem import make_property from virtualsmartcard.utils import C_APDU, R_APDU, hexdump, inttostring from virtualsmartcard.CardGenerator import CardGenerator -import socket, struct, sys, signal, atexit, smartcard, logging +import socket, struct, sys, signal, atexit, logging class SmartcardOS(object): @@ -397,111 +397,6 @@ class CryptoflexOS(Iso7816OS): return r -class RelayOS(SmartcardOS): - """ - This class implements relaying of a (physical) smartcard. The RelayOS - forwards the command APDUs received from the vpcd to the real smartcard via - an actual smart card reader and sends the responses back to the vpcd. - This class can be used to implement relay or MitM attacks. - """ - def __init__(self, readernum): - """ - Initialize the connection to the (physical) smart card via a given reader - """ - - # See which readers are available - readers = smartcard.System.listReaders() - if len(readers) <= readernum: - logging.error("Invalid number of reader '%u' (only %u available)", - readernum, len(readers)) - sys.exit() - - # Connect to the reader and its card - # XXX this is a workaround, see on sourceforge bug #3083254 - # should better use - # self.reader = smartcard.System.readers()[readernum] - self.reader = readers[readernum] - try: - self.session = smartcard.Session(self.reader) - except smartcard.Exceptions.CardConnectionException as e: - logging.error("Error connecting to card: %s", e.message) - sys.exit() - - logging.info("Connected to card in '%s'", self.reader) - - atexit.register(self.cleanup) - - def cleanup(self): - """ - Close the connection to the physical card - """ - try: - self.session.close() - except smartcard.Exceptions.CardConnectionException as e: - logging.warning("Error disconnecting from card: %s", e.message) - - def getATR(self): - # when powerDown has been called, fetching the ATR will throw an error. - # In this case we must try to reconnect (and then get the ATR). - try: - atr = self.session.getATR() - except smartcard.Exceptions.CardConnectionException as e: - try: - # Try to reconnect to the card - self.session.close() - self.session = smartcard.Session(self.reader) - atr = self.session.getATR() - except smartcard.Exceptions.CardConnectionException as e: - logging.error("Error getting ATR: %s", e.message) - sys.exit() - - return "".join([chr(b) for b in atr]) - - def powerUp(self): - # When powerUp is called multiple times the session is valid (and the - # card is implicitly powered) we can check for an ATR. But when - # powerDown has been called, the session gets lost. In this case we - # must try to reconnect (and power the card). - try: - self.session.getATR() - except smartcard.Exceptions.CardConnectionException as e: - try: - self.session = smartcard.Session(self.reader) - except smartcard.Exceptions.CardConnectionException as e: - logging.error("Error connecting to card: %s", e.message) - sys.exit() - - def powerDown(self): - # There is no power down in the session context so we simply - # disconnect, which should implicitly power down the card. - try: - self.session.close() - except smartcard.Exceptions.CardConnectionException as e: - logging.error("Error disconnecting from card: %s", str(e)) - sys.exit() - - def execute(self, msg): - # sendCommandAPDU() expects a list of APDU bytes - apdu = map(ord, msg) - - try: - rapdu, sw1, sw2 = self.session.sendCommandAPDU(apdu) - except smartcard.Exceptions.CardConnectionException as e: - logging.error("Error transmitting APDU: %s", str(e)) - sys.exit() - - # XXX this is a workaround, see on sourceforge bug #3083586 - # should better use - # rapdu = rapdu + [sw1, sw2] - if rapdu[-2:] == [sw1, sw2]: - pass - else: - rapdu = rapdu + [sw1, sw2] - - # return the response APDU as string - return "".join([chr(b) for b in rapdu]) - - class NPAOS(Iso7816OS): def __init__(self, mf, sam, ins2handler=None, maxle=MAX_EXTENDED_LE, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, esign_key=None, esign_ca_cert=None, esign_cert=None): Iso7816OS.__init__(self, mf, sam, ins2handler, maxle) @@ -685,6 +580,7 @@ class VirtualICC(object): elif card_type == "cryptoflex": self.os = CryptoflexOS(MF, SAM) elif card_type == "relay": + from virtualsmartcard.cards.Relay import RelayOS self.os = RelayOS(readernum) elif card_type == "handler_test": self.os = HandlerTestOS() diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py new file mode 100644 index 0000000..6d4eda6 --- /dev/null +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/cards/Relay.py @@ -0,0 +1,126 @@ +# +# Copyright (C) 2015 Frank Morgner +# +# This file is part of virtualsmartcard. +# +# virtualsmartcard is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# virtualsmartcard is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# virtualsmartcard. If not, see . +# + +from virtualsmartcard.VirtualSmartcard import SmartcardOS +import smartcard, logging + + +class RelayOS(SmartcardOS): + """ + This class implements relaying of a (physical) smartcard. The RelayOS + forwards the command APDUs received from the vpcd to the real smartcard via + an actual smart card reader and sends the responses back to the vpcd. + This class can be used to implement relay or MitM attacks. + """ + def __init__(self, readernum): + """ + Initialize the connection to the (physical) smart card via a given reader + """ + + # See which readers are available + readers = smartcard.System.listReaders() + if len(readers) <= readernum: + logging.error("Invalid number of reader '%u' (only %u available)", + readernum, len(readers)) + sys.exit() + + # Connect to the reader and its card + # XXX this is a workaround, see on sourceforge bug #3083254 + # should better use + # self.reader = smartcard.System.readers()[readernum] + self.reader = readers[readernum] + try: + self.session = smartcard.Session(self.reader) + except smartcard.Exceptions.CardConnectionException as e: + logging.error("Error connecting to card: %s", e.message) + sys.exit() + + logging.info("Connected to card in '%s'", self.reader) + + atexit.register(self.cleanup) + + def cleanup(self): + """ + Close the connection to the physical card + """ + try: + self.session.close() + except smartcard.Exceptions.CardConnectionException as e: + logging.warning("Error disconnecting from card: %s", e.message) + + def getATR(self): + # when powerDown has been called, fetching the ATR will throw an error. + # In this case we must try to reconnect (and then get the ATR). + try: + atr = self.session.getATR() + except smartcard.Exceptions.CardConnectionException as e: + try: + # Try to reconnect to the card + self.session.close() + self.session = smartcard.Session(self.reader) + atr = self.session.getATR() + except smartcard.Exceptions.CardConnectionException as e: + logging.error("Error getting ATR: %s", e.message) + sys.exit() + + return "".join([chr(b) for b in atr]) + + def powerUp(self): + # When powerUp is called multiple times the session is valid (and the + # card is implicitly powered) we can check for an ATR. But when + # powerDown has been called, the session gets lost. In this case we + # must try to reconnect (and power the card). + try: + self.session.getATR() + except smartcard.Exceptions.CardConnectionException as e: + try: + self.session = smartcard.Session(self.reader) + except smartcard.Exceptions.CardConnectionException as e: + logging.error("Error connecting to card: %s", e.message) + sys.exit() + + def powerDown(self): + # There is no power down in the session context so we simply + # disconnect, which should implicitly power down the card. + try: + self.session.close() + except smartcard.Exceptions.CardConnectionException as e: + logging.error("Error disconnecting from card: %s", str(e)) + sys.exit() + + def execute(self, msg): + # sendCommandAPDU() expects a list of APDU bytes + apdu = map(ord, msg) + + try: + rapdu, sw1, sw2 = self.session.sendCommandAPDU(apdu) + except smartcard.Exceptions.CardConnectionException as e: + logging.error("Error transmitting APDU: %s", str(e)) + sys.exit() + + # XXX this is a workaround, see on sourceforge bug #3083586 + # should better use + # rapdu = rapdu + [sw1, sw2] + if rapdu[-2:] == [sw1, sw2]: + pass + else: + rapdu = rapdu + [sw1, sw2] + + # return the response APDU as string + return "".join([chr(b) for b in rapdu])