class VirtualICC(object):
    """
    This class is responsible for maintaining the communication of the virtual
    PCD and the emulated smartcard. vpicc and vpcd communicate via a socket.
    The vpcd sends command APDUs (which it receives from an application) to the
    vicc. The vicc passes these CAPDUs on to an emulated smartcard, which
    produces a response APDU. This RAPDU is then passed back by the vicc to
    the vpcd, which forwards it to the application.
    """
    def __init__(self,
                 datasetfile,
                 card_type,
                 host,
                 port,
                 readernum=None,
                 mitmPath=None,
                 ef_cardsecurity=None,
                 ef_cardaccess=None,
                 ca_key=None,
                 cvca=None,
                 disable_checks=False,
                 esign_key=None,
                 esign_ca_cert=None,
                 esign_cert=None,
                 logginglevel=logging.INFO):
        from os.path import exists

        logging.basicConfig(level=logginglevel,
                            format="%(asctime)s  [%(levelname)s] %(message)s",
                            datefmt="%d.%m.%Y %H:%M:%S")

        self.cardGenerator = CardGenerator(card_type)

        # If a dataset file is specified, read the card's data groups from disk
        if datasetfile is not None:
            if exists(datasetfile):
                logging.info("Reading Data Groups from file %s.", datasetfile)
                self.cardGenerator.readDatagroups(datasetfile)

        MF, SAM = self.cardGenerator.getCard()

        # Generate an OS object of the correct card_type
        if card_type == "iso7816" or card_type == "ePass":
            self.os = Iso7816OS(MF, SAM)
        elif card_type == "nPA":
            from virtualsmartcard.cards.nPA import NPAOS
            self.os = NPAOS(MF,
                            SAM,
                            ef_cardsecurity=ef_cardsecurity,
                            ef_cardaccess=ef_cardaccess,
                            ca_key=ca_key,
                            cvca=cvca,
                            disable_checks=disable_checks,
                            esign_key=esign_key,
                            esign_ca_cert=esign_ca_cert,
                            esign_cert=esign_cert)
        elif card_type == "cryptoflex":
            from virtualsmartcard.cards.cryptoflex import CryptoflexOS
            self.os = CryptoflexOS(MF, SAM)
        elif card_type == "relay":
            from virtualsmartcard.cards.Relay import RelayOS
            from virtualsmartcard.cards.RelayMiddleman import RelayMiddleman
            mitm = loadMitMFromPath(mitmPath) if mitmPath else RelayMiddleman()
            self.os = RelayOS(readernum, mitm=mitm)
        elif card_type == "handler_test":
            from virtualsmartcard.cards.HandlerTest import HandlerTestOS
            self.os = HandlerTestOS()
        else:
            logging.warning(
                "Unknown cardtype %s. Will use standard card_type \
                            (ISO 7816)", card_type)
            card_type = "iso7816"
            self.os = Iso7816OS(MF, SAM)
        self.type = card_type

        # Connect to the VPCD
        self.host = host
        self.port = port
        if host:
            # use normal connection mode
            try:
                self.sock = self.connectToPort(host, port)
                self.sock.settimeout(None)
                self.server_sock = None
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error(
                    "Is pcscd running at %s? Is vpcd loaded? Is a \
                              firewall blocking port %u?", host, port)
                sys.exit()
        else:
            # use reversed connection mode
            try:
                local_ip = [
                    (s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close())
                    for s in
                    [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]
                ][0][1]
                custom_url = 'vicc://%s:%d' % (local_ip, port)
                print('VICC hostname:  %s' % local_ip)
                print('VICC port:      %d' % port)
                print(
                    'On your NFC phone with the Android Smart Card Emulator app scan this code:'
                )
                try:
                    import qrcode
                    qr = qrcode.QRCode()
                    qr.add_data(custom_url)
                    qr.print_ascii()
                except ImportError:
                    print(
                        'https://api.qrserver.com/v1/create-qr-code/?data=%s' %
                        custom_url)
                (self.sock, self.server_sock, host) = self.openPort(port)
                self.sock.settimeout(None)
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error(
                    "Is pcscd running? Is vpcd loaded and in \
                              reversed connection mode? Is a firewall \
                              blocking port %u?", port)
                sys.exit()

        logging.info("Connected to virtual PCD at %s:%u", host, port)

        atexit.register(self.stop)

    @staticmethod
    def connectToPort(host, port):
        """
        Open a connection to a given host on a given port.
        """
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, port))
        return sock

    @staticmethod
    def openPort(port):
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('', port))
        server_socket.listen(0)
        logging.info("Waiting for vpcd on port " + str(port))
        (client_socket, address) = server_socket.accept()
        return (client_socket, server_socket, address[0])

    def __sendToVPICC(self, msg):
        """ Send a message to the vpcd """
        if isinstance(msg, str):
            self.sock.sendall(
                struct.pack('!H', len(msg)) + bytes(map(ord, msg)))
        else:
            self.sock.sendall(struct.pack('!H', len(msg)) + msg)

    def __recvFromVPICC(self):
        """ Receive a message from the vpcd """
        # receive message size
        while True:
            try:
                sizestr = self.sock.recv(_Csizeof_short)
            except socket.error as e:
                if e.errno == errno.EINTR:
                    continue
            break
        if len(sizestr) == 0:
            logging.info("Virtual PCD shut down")
            raise socket.error
        size = struct.unpack('!H', sizestr)[0]

        # receive and return message
        if size:
            while True:
                try:
                    msg = self.sock.recv(size)
                except socket.error as e:
                    if e.errno == errno.EINTR:
                        continue
                break
            if len(msg) == 0:
                logging.info("Virtual PCD shut down")
                raise socket.error
        else:
            msg = None

        return size, msg

    def run(self):
        """
        Main loop of the vpicc. Receives command APDUs via a socket from the
        vpcd, dispatches them to the emulated smartcard and sends the resulting
        respsonse APDU back to the vpcd.
        """
        while True:
            try:
                (size, msg) = self.__recvFromVPICC()
            except socket.error as e:
                if not self.host:
                    logging.info("Waiting for vpcd on port " + str(self.port))
                    (self.sock, address) = self.server_sock.accept()
                    continue
                else:
                    sys.exit()

            if not size:
                logging.warning("Error in communication protocol (missing \
                                size parameter)")
            elif size == VPCD_CTRL_LEN:
                if msg == inttostring(VPCD_CTRL_OFF):
                    logging.info("Power Down")
                    self.os.powerDown()
                elif msg == inttostring(VPCD_CTRL_ON):
                    logging.info("Power Up")
                    self.os.powerUp()
                elif msg == inttostring(VPCD_CTRL_RESET):
                    logging.info("Reset")
                    self.os.reset()
                elif msg == inttostring(VPCD_CTRL_ATR):
                    self.__sendToVPICC(self.os.getATR())
                else:
                    logging.warning("unknown control command")
            else:
                if size != len(msg):
                    logging.warning("Expected %u bytes, but received only %u",
                                    size, len(msg))

                answer = self.os.execute(msg)
                logging.info("Response APDU (%d bytes):\n  %s\n", len(answer),
                             hexdump(answer, indent=2))
                self.__sendToVPICC(answer)

    def stop(self):
        self.sock.close()
        if self.server_sock:
            self.server_sock.close()
    def __init__(self,
                 datasetfile,
                 card_type,
                 host,
                 port,
                 readernum=None,
                 mitmPath=None,
                 ef_cardsecurity=None,
                 ef_cardaccess=None,
                 ca_key=None,
                 cvca=None,
                 disable_checks=False,
                 esign_key=None,
                 esign_ca_cert=None,
                 esign_cert=None,
                 logginglevel=logging.INFO):
        from os.path import exists

        logging.basicConfig(level=logginglevel,
                            format="%(asctime)s  [%(levelname)s] %(message)s",
                            datefmt="%d.%m.%Y %H:%M:%S")

        self.cardGenerator = CardGenerator(card_type)

        # If a dataset file is specified, read the card's data groups from disk
        if datasetfile is not None:
            if exists(datasetfile):
                logging.info("Reading Data Groups from file %s.", datasetfile)
                self.cardGenerator.readDatagroups(datasetfile)

        MF, SAM = self.cardGenerator.getCard()

        # Generate an OS object of the correct card_type
        if card_type == "iso7816" or card_type == "ePass":
            self.os = Iso7816OS(MF, SAM)
        elif card_type == "nPA":
            from virtualsmartcard.cards.nPA import NPAOS
            self.os = NPAOS(MF,
                            SAM,
                            ef_cardsecurity=ef_cardsecurity,
                            ef_cardaccess=ef_cardaccess,
                            ca_key=ca_key,
                            cvca=cvca,
                            disable_checks=disable_checks,
                            esign_key=esign_key,
                            esign_ca_cert=esign_ca_cert,
                            esign_cert=esign_cert)
        elif card_type == "cryptoflex":
            from virtualsmartcard.cards.cryptoflex import CryptoflexOS
            self.os = CryptoflexOS(MF, SAM)
        elif card_type == "relay":
            from virtualsmartcard.cards.Relay import RelayOS
            from virtualsmartcard.cards.RelayMiddleman import RelayMiddleman
            mitm = loadMitMFromPath(mitmPath) if mitmPath else RelayMiddleman()
            self.os = RelayOS(readernum, mitm=mitm)
        elif card_type == "handler_test":
            from virtualsmartcard.cards.HandlerTest import HandlerTestOS
            self.os = HandlerTestOS()
        else:
            logging.warning(
                "Unknown cardtype %s. Will use standard card_type \
                            (ISO 7816)", card_type)
            card_type = "iso7816"
            self.os = Iso7816OS(MF, SAM)
        self.type = card_type

        # Connect to the VPCD
        self.host = host
        self.port = port
        if host:
            # use normal connection mode
            try:
                self.sock = self.connectToPort(host, port)
                self.sock.settimeout(None)
                self.server_sock = None
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error(
                    "Is pcscd running at %s? Is vpcd loaded? Is a \
                              firewall blocking port %u?", host, port)
                sys.exit()
        else:
            # use reversed connection mode
            try:
                local_ip = [
                    (s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close())
                    for s in
                    [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]
                ][0][1]
                custom_url = 'vicc://%s:%d' % (local_ip, port)
                print('VICC hostname:  %s' % local_ip)
                print('VICC port:      %d' % port)
                print(
                    'On your NFC phone with the Android Smart Card Emulator app scan this code:'
                )
                try:
                    import qrcode
                    qr = qrcode.QRCode()
                    qr.add_data(custom_url)
                    qr.print_ascii()
                except ImportError:
                    print(
                        'https://api.qrserver.com/v1/create-qr-code/?data=%s' %
                        custom_url)
                (self.sock, self.server_sock, host) = self.openPort(port)
                self.sock.settimeout(None)
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error(
                    "Is pcscd running? Is vpcd loaded and in \
                              reversed connection mode? Is a firewall \
                              blocking port %u?", port)
                sys.exit()

        logging.info("Connected to virtual PCD at %s:%u", host, port)

        atexit.register(self.stop)
Beispiel #3
0
    def __init__(self, filename, datasetfile, card_type, host, port, readernum=None, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, esign_key=None, esign_ca_cert=None, esign_cert=None, logginglevel=logging.INFO):
        from os.path import exists
        
        logging.basicConfig(level = logginglevel, 
                            format = "%(asctime)s  [%(levelname)s] %(message)s", 
                            datefmt = "%d.%m.%Y %H:%M:%S") 
        
        self.filename = None
        self.cardGenerator = CardGenerator(card_type)
        
        #If a filename is specified, try to load the card from disk      
        if filename != None:
            self.filename = filename
            if exists(filename):
                self.cardGenerator.loadCard(self.filename)
            else:
                logging.info("Creating new card which will be saved in %s.",
                              self.filename)

        #If a dataset file is specified, read the card's data groups from disk
        if datasetfile != None:
            if exists(datasetfile):
                logging.info("Reading Data Groups from file %s.",
                        datasetfile)
                self.cardGenerator.readDatagroups(datasetfile)

        MF, SAM = self.cardGenerator.getCard()
        
        #Generate an OS object of the correct card_type
        if card_type == "iso7816" or card_type == "ePass":
            self.os = Iso7816OS(MF, SAM)
        elif card_type == "nPA":
            from virtualsmartcard.cards.nPA import NPAOS
            self.os = NPAOS(MF, SAM, ef_cardsecurity=ef_cardsecurity, ef_cardaccess=ef_cardaccess, ca_key=ca_key, cvca=cvca, disable_checks=disable_checks, esign_key=esign_key, esign_ca_cert=esign_ca_cert, esign_cert=esign_cert)
        elif card_type == "cryptoflex":
            from virtualsmartcard.cards.cryptoflex import CryptoflexOS
            self.os = CryptoflexOS(MF, SAM)
        elif card_type == "relay":
            from virtualsmartcard.cards.Relay import RelayOS
            self.os = RelayOS(readernum)
        elif card_type == "handler_test":
            from virtualsmartcard.cards.HandlerTest import HandlerTestOS
            self.os = HandlerTestOS()
        else:
            logging.warning("Unknown cardtype %s. Will use standard card_type (ISO 7816)",
                            card_type)
            card_type = "iso7816"
            self.os = Iso7816OS(MF, SAM)
        self.type = card_type
            
        #Connect to the VPCD
        self.host = host
        self.port = port
        if host:
            # use normal connection mode
            try:
                self.sock = self.connectToPort(host, port)
                self.sock.settimeout(None)
                self.server_sock = None
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error("Is pcscd running at %s? Is vpcd loaded? Is a firewall blocking port %u?",
                              host, port)
                sys.exit()
        else:
            # use reversed connection mode
            try:
                (self.sock, self.server_sock, host) = self.openPort(port)
                self.sock.settimeout(None)
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error("Is pcscd running? Is vpcd loaded and in reversed connection mode? Is a firewall blocking port %u?",
                              port)
                sys.exit()

        logging.info("Connected to virtual PCD at %s:%u", host, port)

        signal.signal(signal.SIGINT, self.signalHandler)
        atexit.register(self.stop)
Beispiel #4
0
class VirtualICC(object): 
    """
    This class is responsible for maintaining the communication of the virtual
    PCD and the emulated smartcard. vpicc and vpcd communicate via a socket.
    The vpcd sends command APDUs (which it receives from an application) to the
    vicc. The vicc passes these CAPDUs on to an emulated smartcard, which
    produces a response APDU. This RAPDU is then passed back by the vicc to
    the vpcd, which forwards it to the application.
    """ 
    
    def __init__(self, filename, datasetfile, card_type, host, port, readernum=None, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, esign_key=None, esign_ca_cert=None, esign_cert=None, logginglevel=logging.INFO):
        from os.path import exists
        
        logging.basicConfig(level = logginglevel, 
                            format = "%(asctime)s  [%(levelname)s] %(message)s", 
                            datefmt = "%d.%m.%Y %H:%M:%S") 
        
        self.filename = None
        self.cardGenerator = CardGenerator(card_type)
        
        #If a filename is specified, try to load the card from disk      
        if filename != None:
            self.filename = filename
            if exists(filename):
                self.cardGenerator.loadCard(self.filename)
            else:
                logging.info("Creating new card which will be saved in %s.",
                              self.filename)

        #If a dataset file is specified, read the card's data groups from disk
        if datasetfile != None:
            if exists(datasetfile):
                logging.info("Reading Data Groups from file %s.",
                        datasetfile)
                self.cardGenerator.readDatagroups(datasetfile)

        MF, SAM = self.cardGenerator.getCard()
        
        #Generate an OS object of the correct card_type
        if card_type == "iso7816" or card_type == "ePass":
            self.os = Iso7816OS(MF, SAM)
        elif card_type == "nPA":
            from virtualsmartcard.cards.nPA import NPAOS
            self.os = NPAOS(MF, SAM, ef_cardsecurity=ef_cardsecurity, ef_cardaccess=ef_cardaccess, ca_key=ca_key, cvca=cvca, disable_checks=disable_checks, esign_key=esign_key, esign_ca_cert=esign_ca_cert, esign_cert=esign_cert)
        elif card_type == "cryptoflex":
            from virtualsmartcard.cards.cryptoflex import CryptoflexOS
            self.os = CryptoflexOS(MF, SAM)
        elif card_type == "relay":
            from virtualsmartcard.cards.Relay import RelayOS
            self.os = RelayOS(readernum)
        elif card_type == "handler_test":
            from virtualsmartcard.cards.HandlerTest import HandlerTestOS
            self.os = HandlerTestOS()
        else:
            logging.warning("Unknown cardtype %s. Will use standard card_type (ISO 7816)",
                            card_type)
            card_type = "iso7816"
            self.os = Iso7816OS(MF, SAM)
        self.type = card_type
            
        #Connect to the VPCD
        self.host = host
        self.port = port
        if host:
            # use normal connection mode
            try:
                self.sock = self.connectToPort(host, port)
                self.sock.settimeout(None)
                self.server_sock = None
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error("Is pcscd running at %s? Is vpcd loaded? Is a firewall blocking port %u?",
                              host, port)
                sys.exit()
        else:
            # use reversed connection mode
            try:
                (self.sock, self.server_sock, host) = self.openPort(port)
                self.sock.settimeout(None)
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error("Is pcscd running? Is vpcd loaded and in reversed connection mode? Is a firewall blocking port %u?",
                              port)
                sys.exit()

        logging.info("Connected to virtual PCD at %s:%u", host, port)

        signal.signal(signal.SIGINT, self.signalHandler)
        atexit.register(self.stop)
    
    def signalHandler(self, sig=None, frame=None):
        """Basically this signal handler just surpresses a traceback from being
           printed when the user presses crtl-c"""
        sys.exit()

    @staticmethod
    def connectToPort(host, port):
        """
        Open a connection to a given host on a given port.
        """
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, port))
        return sock

    @staticmethod
    def openPort(port):
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('', port))
        server_socket.listen(0)
        logging.info("Waiting for vpcd on port " + str(port))
        (client_socket, address) = server_socket.accept()
        return (client_socket, server_socket, address[0])

    def __sendToVPICC(self, msg):
        """ Send a message to the vpcd """
        self.sock.sendall(struct.pack('!H', len(msg)) + msg)

    def __recvFromVPICC(self):
        """ Receive a message from the vpcd """
        # receive message size
        sizestr = self.sock.recv(_Csizeof_short)
        if len(sizestr) == 0:
            logging.info("Virtual PCD shut down")
            raise socket.error
        size = struct.unpack('!H', sizestr)[0]

        # receive and return message
        if size:
            msg = self.sock.recv(size)
            if len(msg) == 0:
                logging.info("Virtual PCD shut down")
                raise socket.error
        else:
            msg = None

        return size, msg

    def run(self):
        """
        Main loop of the vpicc. Receives command APDUs via a socket from the
        vpcd, dispatches them to the emulated smartcard and sends the resulting
        respsonse APDU back to the vpcd.
        """
        while True :
            try:
                (size, msg) = self.__recvFromVPICC()
            except socket.error as e:
                if not self.host:
                    logging.info("Waiting for vpcd on port " + str(self.port))
                    (self.sock, address) = self.server_sock.accept()
                    continue
                else:
                    sys.exit()

            if not size:
                logging.warning("Error in communication protocol (missing size parameter)")
            elif size == VPCD_CTRL_LEN:
                if msg == chr(VPCD_CTRL_OFF):
                    logging.info("Power Down")
                    self.os.powerDown()
                elif msg == chr(VPCD_CTRL_ON):
                    logging.info("Power Up")
                    self.os.powerUp()
                elif msg == chr(VPCD_CTRL_RESET):
                    logging.info("Reset")
                    self.os.reset()
                elif msg == chr(VPCD_CTRL_ATR):
                    self.__sendToVPICC(self.os.getATR())
                else:
                    logging.warning("unknown control command")
            else:
                if size != len(msg):
                    logging.warning("Expected %u bytes, but received only %u",
                                    size, len(msg))

                answer = self.os.execute(msg)
                logging.info("Response APDU (%d Bytes):\n%s\n", len(answer),
                             hexdump(answer))
                self.__sendToVPICC(answer)

    def stop(self):
        self.sock.close()
        if self.server_sock:
            self.server_sock.close()
        if self.filename != None:
            self.cardGenerator.setCard(self.os.mf, self.os.SAM)
            self.cardGenerator.saveCard(self.filename)
class VirtualICC(object):
    """
    This class is responsible for maintaining the communication of the virtual
    PCD and the emulated smartcard. vpicc and vpcd communicate via a socket.
    The vpcd sends command APDUs (which it receives from an application) to the
    vicc. The vicc passes these CAPDUs on to an emulated smartcard, which
    produces a response APDU. This RAPDU is then passed back by the vicc to
    the vpcd, which forwards it to the application.
    """
    def __init__(
            self,
            datasetfile,
            card_type,
            host,
            port,
            mode,
            localIP,  # MODIFIED ARGUMENTS
            readernum=None,
            ef_cardsecurity=None,
            ef_cardaccess=None,
            ca_key=None,
            cvca=None,
            disable_checks=False,
            esign_key=None,
            esign_ca_cert=None,
            esign_cert=None,
            logginglevel=logging.INFO):
        from os.path import exists

        logging.basicConfig(level=logginglevel,
                            format="%(asctime)s  [%(levelname)s] %(message)s",
                            datefmt="%d.%m.%Y %H:%M:%S")

        self.cardGenerator = CardGenerator(card_type)

        # If a dataset file is specified, read the card's data groups from disk
        if datasetfile is not None:
            if exists(datasetfile):
                logging.info("Reading Data Groups from file %s.", datasetfile)
                self.cardGenerator.readDatagroups(datasetfile)

        MF, SAM = self.cardGenerator.getCard()

        # Generate an OS object of the correct card_type
        if card_type == "iso7816" or card_type == "ePass":
            self.os = Iso7816OS(MF, SAM)
        elif card_type == "nPA":
            from virtualsmartcard.cards.nPA import NPAOS
            self.os = NPAOS(MF,
                            SAM,
                            ef_cardsecurity=ef_cardsecurity,
                            ef_cardaccess=ef_cardaccess,
                            ca_key=ca_key,
                            cvca=cvca,
                            disable_checks=disable_checks,
                            esign_key=esign_key,
                            esign_ca_cert=esign_ca_cert,
                            esign_cert=esign_cert)
        elif card_type == "cryptoflex":
            from virtualsmartcard.cards.cryptoflex import CryptoflexOS
            self.os = CryptoflexOS(MF, SAM)
        elif card_type == "relay":
            from virtualsmartcard.cards.Relay import RelayOS
            self.os = RelayOS(readernum)
        elif card_type == "handler_test":
            from virtualsmartcard.cards.HandlerTest import HandlerTestOS
            self.os = HandlerTestOS()
        else:
            logging.warning(
                "Unknown cardtype %s. Will use standard card_type \
                            (ISO 7816)", card_type)
            card_type = "iso7816"
            self.os = Iso7816OS(MF, SAM)
        self.type = card_type

        # Connect to the VPCD
        self.host = host
        self.port = port
        if host:
            # use normal connection mode
            try:
                self.sock = self.connectToPort(host, port)
                self.sock.settimeout(None)
                self.server_sock = None
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error(
                    "Is pcscd running at %s? Is vpcd loaded? Is a \
                              firewall blocking port %u?", host, port)
                sys.exit()
        else:
            # use reversed connection mode
            try:
                local_ip = [
                    (s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close())
                    for s in
                    [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]
                ][0][1]
                custom_url = 'vicc://%s:%d' % (local_ip, port)
                print('VICC hostname:  %s' % local_ip)
                print('VICC port:      %d' % port)
                print(
                    'On your NFC phone with the Android Smart Card Emulator app scan this code:'
                )
                try:
                    import qrcode
                    qr = qrcode.QRCode()
                    qr.add_data(custom_url)
                    qr.print_ascii()
                except ImportError:
                    print(
                        'https://api.qrserver.com/v1/create-qr-code/?data=%s' %
                        custom_url)
                (self.sock, self.server_sock, host) = self.openPort(port)
                self.sock.settimeout(None)
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error(
                    "Is pcscd running? Is vpcd loaded and in \
                              reversed connection mode? Is a firewall \
                              blocking port %u?", port)
                sys.exit()

        logging.info("Connected to virtual PCD at %s:%u", host, port)

        # ADDED CODE SECTION IN ORDER TO INTEGRATE ESP32 TO GNUPG STARTS HERE
        if (mode == "esp"):
            SocketServer.TCPServer.allow_reuse_address = True
            server = SocketServer.TCPServer((localIP, 5511), handleConnection)
            srvThrd = threading.Thread(target=server.serve_forever)
            srvThrd.daemon = True
            srvThrd.start()
        # ADDED CODE SECTION ENDS HERE

        atexit.register(self.stop)

    @staticmethod
    def connectToPort(host, port):
        """
        Open a connection to a given host on a given port.
        """
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, port))
        return sock

    @staticmethod
    def openPort(port):
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('', port))
        server_socket.listen(0)
        logging.info("Waiting for vpcd on port " + str(port))
        (client_socket, address) = server_socket.accept()
        return (client_socket, server_socket, address[0])

    def __sendToVPICC(self, msg):
        """ Send a message to the vpcd """
        self.sock.sendall(struct.pack('!H', len(msg)) + msg)

    def __recvFromVPICC(self):
        """ Receive a message from the vpcd """
        # receive message size
        while True:
            try:
                sizestr = self.sock.recv(_Csizeof_short)
            except socket.error as e:
                if e.errno == errno.EINTR:
                    continue
            break
        if len(sizestr) == 0:
            logging.info("Virtual PCD shut down")
            raise socket.error
        size = struct.unpack('!H', sizestr)[0]

        # receive and return message
        if size:
            while True:
                try:
                    msg = self.sock.recv(size)
                except socket.error as e:
                    if e.errno == errno.EINTR:
                        continue
                break
            if len(msg) == 0:
                logging.info("Virtual PCD shut down")
                raise socket.error
        else:
            msg = None

        return size, msg

    def run(self, mode):  # MODIFIED ARGUMENTS
        """
        Main loop of the vpicc. Receives command APDUs via a socket from the
        vpcd, dispatches them to the emulated smartcard and sends the resulting
        respsonse APDU back to the vpcd.
        """

        # ADDED CODE SECTION IN ORDER TO INTEGRATE ESP32 TO GNUPG STARTS HERE
        global command  # The command APDU
        global response  # The response APDU
        global condCommand  # Condition to wait until a new APDU command arrives
        global condResponse  # Condition to wait until a response is available
        global newCommand  # Flag for the handler that there is a new command
        global processing  # Flag for the run function that the processing has finished
        global err  # Flag for the run function that an error happened
        condCommand = threading.Condition()
        condResponse = threading.Condition()
        # ADDED CODE SECTION ENDS HERE

        while True:
            try:
                (size, msg) = self.__recvFromVPICC()
            except socket.error as e:
                if not self.host:
                    logging.info("Waiting for vpcd on port " + str(self.port))
                    (self.sock, address) = self.server_sock.accept()
                    continue
                else:
                    sys.exit()

            # ADDED CODE SECTION IN ORDER TO INTEGRATE ESP32 TO GNUPG STARTS HERE
            newCommand = 0
            processing = 0
            command = ""
            response = ""
            err = 0
            # ADDED CODE SECTION ENDS HERE

            if not size:
                logging.warning("Error in communication protocol (missing \
                                size parameter)")
            elif size == VPCD_CTRL_LEN:
                if msg == chr(VPCD_CTRL_OFF):
                    logging.info("Power Down")
                    self.os.powerDown()
                elif msg == chr(VPCD_CTRL_ON):
                    logging.info("Power Up")
                    self.os.powerUp()
                elif msg == chr(VPCD_CTRL_RESET):
                    logging.info("Reset")
                    # ADDED CODE SECTION IN ORDER TO INTEGRATE ESP32 TO GNUPG STARTS HERE
                    if (mode == "esp"):
                        with condCommand:
                            command = '\x00\x55\x00\x00\x00'  # Custom command INS to reset
                            newCommand = 1
                            processing = 1
                            condCommand.notify()

                        with condResponse:
                            while (processing == 1):
                                condResponse.wait()
                    # ADDED CODE SECTION ENDS HERE
                    self.os.reset()
                elif msg == chr(VPCD_CTRL_ATR):
                    self.__sendToVPICC(self.os.getATR())
                else:
                    logging.warning("unknown control command")
            else:
                if size != len(msg):
                    logging.warning("Expected %u bytes, but received only %u",
                                    size, len(msg))

                # ADDED CODE SECTION IN ORDER TO INTEGRATE ESP32 TO GNUPG STARTS HERE
                if (mode == "esp"):
                    with condCommand:
                        command = msg
                        newCommand = 1
                        processing = 1
                        condCommand.notify()

                    with condResponse:
                        while (processing == 1):
                            condResponse.wait(0)

                        if (err == 0):
                            self.__sendToVPICC(response)
                        else:  # ESP32 was probably disconnected
                            sys.exit()  # Terminate execution
                else:
                    # ADDED CODE SECTION ENDS HERE
                    answer = self.os.execute(msg)
                    logging.info("Response APDU (%d Bytes):\n%s\n",
                                 len(answer), hexdump(answer))
                    self.__sendToVPICC(answer)

    def stop(self):
        self.sock.close()
        if self.server_sock:
            self.server_sock.close()
    def __init__(self, datasetfile, card_type, host, port,
                 readernum=None, ef_cardsecurity=None, ef_cardaccess=None,
                 ca_key=None, cvca=None, disable_checks=False, esign_key=None,
                 esign_ca_cert=None, esign_cert=None,
                 logginglevel=logging.INFO):
        from os.path import exists

        logging.basicConfig(level=logginglevel,
                            format="%(asctime)s  [%(levelname)s] %(message)s",
                            datefmt="%d.%m.%Y %H:%M:%S")

        self.cardGenerator = CardGenerator(card_type)

        # If a dataset file is specified, read the card's data groups from disk
        if datasetfile is not None:
            if exists(datasetfile):
                logging.info("Reading Data Groups from file %s.",
                             datasetfile)
                self.cardGenerator.readDatagroups(datasetfile)

        MF, SAM = self.cardGenerator.getCard()

        # Generate an OS object of the correct card_type
        if card_type == "iso7816" or card_type == "ePass":
            self.os = Iso7816OS(MF, SAM)
        elif card_type == "nPA":
            from virtualsmartcard.cards.nPA import NPAOS
            self.os = NPAOS(MF, SAM, ef_cardsecurity=ef_cardsecurity,
                            ef_cardaccess=ef_cardaccess, ca_key=ca_key,
                            cvca=cvca, disable_checks=disable_checks,
                            esign_key=esign_key, esign_ca_cert=esign_ca_cert,
                            esign_cert=esign_cert)
        elif card_type == "cryptoflex":
            from virtualsmartcard.cards.cryptoflex import CryptoflexOS
            self.os = CryptoflexOS(MF, SAM)
        elif card_type == "relay":
            from virtualsmartcard.cards.Relay import RelayOS
            self.os = RelayOS(readernum)
        elif card_type == "handler_test":
            from virtualsmartcard.cards.HandlerTest import HandlerTestOS
            self.os = HandlerTestOS()
        else:
            logging.warning("Unknown cardtype %s. Will use standard card_type \
                            (ISO 7816)", card_type)
            card_type = "iso7816"
            self.os = Iso7816OS(MF, SAM)
        self.type = card_type

        # Connect to the VPCD
        self.host = host
        self.port = port
        if host:
            # use normal connection mode
            try:
                self.sock = self.connectToPort(host, port)
                self.sock.settimeout(None)
                self.server_sock = None
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error("Is pcscd running at %s? Is vpcd loaded? Is a \
                              firewall blocking port %u?", host, port)
                sys.exit()
        else:
            # use reversed connection mode
            try:
                local_ip = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
                custom_url = 'vicc://%s:%d' % (local_ip, port)
                print('VICC hostname:  %s' % local_ip)
                print('VICC port:      %d' % port)
                print('On your NFC phone with the Android Smart Card Emulator app scan this code:')
                try:
                    import qrcode
                    qr = qrcode.QRCode()
                    qr.add_data(custom_url)
                    qr.print_ascii()
                except ImportError:
                    print('https://api.qrserver.com/v1/create-qr-code/?data=%s' % custom_url)
                (self.sock, self.server_sock, host) = self.openPort(port)
                self.sock.settimeout(None)
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error("Is pcscd running? Is vpcd loaded and in \
                              reversed connection mode? Is a firewall \
                              blocking port %u?", port)
                sys.exit()

        logging.info("Connected to virtual PCD at %s:%u", host, port)

        atexit.register(self.stop)
class VirtualICC(object):
    """
    This class is responsible for maintaining the communication of the virtual
    PCD and the emulated smartcard. vpicc and vpcd communicate via a socket.
    The vpcd sends command APDUs (which it receives from an application) to the
    vicc. The vicc passes these CAPDUs on to an emulated smartcard, which
    produces a response APDU. This RAPDU is then passed back by the vicc to
    the vpcd, which forwards it to the application.
    """

    def __init__(self, datasetfile, card_type, host, port,
                 readernum=None, ef_cardsecurity=None, ef_cardaccess=None,
                 ca_key=None, cvca=None, disable_checks=False, esign_key=None,
                 esign_ca_cert=None, esign_cert=None,
                 logginglevel=logging.INFO):
        from os.path import exists

        logging.basicConfig(level=logginglevel,
                            format="%(asctime)s  [%(levelname)s] %(message)s",
                            datefmt="%d.%m.%Y %H:%M:%S")

        self.cardGenerator = CardGenerator(card_type)

        # If a dataset file is specified, read the card's data groups from disk
        if datasetfile is not None:
            if exists(datasetfile):
                logging.info("Reading Data Groups from file %s.",
                             datasetfile)
                self.cardGenerator.readDatagroups(datasetfile)

        MF, SAM = self.cardGenerator.getCard()

        # Generate an OS object of the correct card_type
        if card_type == "iso7816" or card_type == "ePass":
            self.os = Iso7816OS(MF, SAM)
        elif card_type == "nPA":
            from virtualsmartcard.cards.nPA import NPAOS
            self.os = NPAOS(MF, SAM, ef_cardsecurity=ef_cardsecurity,
                            ef_cardaccess=ef_cardaccess, ca_key=ca_key,
                            cvca=cvca, disable_checks=disable_checks,
                            esign_key=esign_key, esign_ca_cert=esign_ca_cert,
                            esign_cert=esign_cert)
        elif card_type == "cryptoflex":
            from virtualsmartcard.cards.cryptoflex import CryptoflexOS
            self.os = CryptoflexOS(MF, SAM)
        elif card_type == "relay":
            from virtualsmartcard.cards.Relay import RelayOS
            self.os = RelayOS(readernum)
        elif card_type == "handler_test":
            from virtualsmartcard.cards.HandlerTest import HandlerTestOS
            self.os = HandlerTestOS()
        else:
            logging.warning("Unknown cardtype %s. Will use standard card_type \
                            (ISO 7816)", card_type)
            card_type = "iso7816"
            self.os = Iso7816OS(MF, SAM)
        self.type = card_type

        # Connect to the VPCD
        self.host = host
        self.port = port
        if host:
            # use normal connection mode
            try:
                self.sock = self.connectToPort(host, port)
                self.sock.settimeout(None)
                self.server_sock = None
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error("Is pcscd running at %s? Is vpcd loaded? Is a \
                              firewall blocking port %u?", host, port)
                sys.exit()
        else:
            # use reversed connection mode
            try:
                local_ip = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]
                custom_url = 'vicc://%s:%d' % (local_ip, port)
                print('VICC hostname:  %s' % local_ip)
                print('VICC port:      %d' % port)
                print('On your NFC phone with the Android Smart Card Emulator app scan this code:')
                try:
                    import qrcode
                    qr = qrcode.QRCode()
                    qr.add_data(custom_url)
                    qr.print_ascii()
                except ImportError:
                    print('https://api.qrserver.com/v1/create-qr-code/?data=%s' % custom_url)
                (self.sock, self.server_sock, host) = self.openPort(port)
                self.sock.settimeout(None)
            except socket.error as e:
                logging.error("Failed to open socket: %s", str(e))
                logging.error("Is pcscd running? Is vpcd loaded and in \
                              reversed connection mode? Is a firewall \
                              blocking port %u?", port)
                sys.exit()

        logging.info("Connected to virtual PCD at %s:%u", host, port)

        atexit.register(self.stop)

    @staticmethod
    def connectToPort(host, port):
        """
        Open a connection to a given host on a given port.
        """
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((host, port))
        return sock

    @staticmethod
    def openPort(port):
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('', port))
        server_socket.listen(0)
        logging.info("Waiting for vpcd on port " + str(port))
        (client_socket, address) = server_socket.accept()
        return (client_socket, server_socket, address[0])

    def __sendToVPICC(self, msg):
        """ Send a message to the vpcd """
        self.sock.sendall(struct.pack('!H', len(msg)) + msg)

    def __recvFromVPICC(self):
        """ Receive a message from the vpcd """
        # receive message size
        while True:
            try:
                sizestr = self.sock.recv(_Csizeof_short)
            except socket.error as e:
                if e.errno == errno.EINTR:
                    continue
            break
        if len(sizestr) == 0:
            logging.info("Virtual PCD shut down")
            raise socket.error
        size = struct.unpack('!H', sizestr)[0]

        # receive and return message
        if size:
            while True:
                try:
                    msg = self.sock.recv(size)
                except socket.error as e:
                    if e.errno == errno.EINTR:
                        continue
                break
            if len(msg) == 0:
                logging.info("Virtual PCD shut down")
                raise socket.error
        else:
            msg = None

        return size, msg

    def run(self):
        """
        Main loop of the vpicc. Receives command APDUs via a socket from the
        vpcd, dispatches them to the emulated smartcard and sends the resulting
        respsonse APDU back to the vpcd.
        """
        while True:
            try:
                (size, msg) = self.__recvFromVPICC()
            except socket.error as e:
                if not self.host:
                    logging.info("Waiting for vpcd on port " + str(self.port))
                    (self.sock, address) = self.server_sock.accept()
                    continue
                else:
                    sys.exit()

            if not size:
                logging.warning("Error in communication protocol (missing \
                                size parameter)")
            elif size == VPCD_CTRL_LEN:
                if msg == inttostring(VPCD_CTRL_OFF):
                    logging.info("Power Down")
                    self.os.powerDown()
                elif msg == inttostring(VPCD_CTRL_ON):
                    logging.info("Power Up")
                    self.os.powerUp()
                elif msg == inttostring(VPCD_CTRL_RESET):
                    logging.info("Reset")
                    self.os.reset()
                elif msg == inttostring(VPCD_CTRL_ATR):
                    self.__sendToVPICC(self.os.getATR())
                else:
                    logging.warning("unknown control command")
            else:
                if size != len(msg):
                    logging.warning("Expected %u bytes, but received only %u",
                                    size, len(msg))

                answer = self.os.execute(msg)
                logging.info("Response APDU (%d bytes):\n  %s\n", len(answer),
                             hexdump(answer, indent=2))
                self.__sendToVPICC(answer)

    def stop(self):
        self.sock.close()
        if self.server_sock:
            self.server_sock.close()