Пример #1
0
class CW305(TargetTemplate):
    _name = "ChipWhisperer CW305 (Artix-7)"

    def __init__(self):
        TargetTemplate.__init__(self)
        self._naeusb = NAEUSB()
        self.pll = PLLCDCE906(self._naeusb, ref_freq=12.0E6, parent=self)
        self.fpga = FPGA(self._naeusb)

        self.hw = None
        # self._fpgabs = QSettings().value("cw305-bitstream", '')
        self.oa = None

        self._woffset = 0x400

        self.params.addChildren([
            {
                'name':
                'PLL Settings',
                'key':
                'pll',
                'type':
                'group',
                'children': [
                    {
                        'name': 'Enabled',
                        'key': 'pllenabled',
                        'type': 'bool',
                        'default': False,
                        'set': self.pll.pll_enable_set,
                        'get': self.pll.pll_enable_get,
                        'psync': False
                    },
                    {
                        'name':
                        'CLK-SMA (X6)',
                        'key':
                        'pll0',
                        'type':
                        'group',
                        'children': [
                            {
                                'name':
                                'CLK-SMA Enabled',
                                'key':
                                'pll0enabled',
                                'type':
                                'bool',
                                'default':
                                False,
                                'set':
                                partial(self.pll.pll_outenable_set, outnum=0),
                                'get':
                                partial(self.pll.pll_outenable_get, outnum=0),
                                'psync':
                                False
                            },
                            {
                                'name':
                                'CLK-SMA Source',
                                'key':
                                'pll0source',
                                'type':
                                'list',
                                'values': ['PLL0', 'PLL1', 'PLL2'],
                                'default':
                                'PLL0',
                                'set':
                                partial(self.pll.pll_outsource_set, outnum=0),
                                'get':
                                partial(self.pll.pll_outsource_get, outnum=0),
                                'psync':
                                False
                            },
                            {
                                'name': 'CLK-SMA Slew Rate',
                                'key': 'pll0slew',
                                'type': 'list',
                                'values': ['+3nS', '+2nS', '+1nS', '+0nS'],
                                'default': '+0nS',
                                'set': partial(self.pll.pll_outslew_set,
                                               outnum=0),
                                'get': partial(self.pll.pll_outslew_get,
                                               outnum=0),
                                'psync': False
                            },
                            {
                                'name': 'PLL0 Frequency',
                                'key': 'pll0freq',
                                'type': 'float',
                                'limits': (0.625E6, 167E6),
                                'default': 0,
                                'step': 1E6,
                                'siPrefix': True,
                                'suffix': 'Hz',
                                'set': partial(self.pll.pll_outfreq_set,
                                               outnum=0),
                                'get': partial(self.pll.pll_outfreq_get,
                                               outnum=0),
                                'psync': False
                            },
                        ]
                    },
                    {
                        'name':
                        'CLK-N13 (FGPA Pin N13)',
                        'key':
                        'pll1',
                        'type':
                        'group',
                        'children': [
                            {
                                'name':
                                'CLK-N13 Enabled',
                                'key':
                                'pll1enabled',
                                'type':
                                'bool',
                                'default':
                                False,
                                'set':
                                partial(self.pll.pll_outenable_set, outnum=1),
                                'get':
                                partial(self.pll.pll_outenable_get, outnum=1),
                                'psync':
                                False
                            },
                            {
                                'name': 'CLK-N13 Source',
                                'key': 'pll1source',
                                'type': 'list',
                                'values': ['PLL1'],
                                'value': 'PLL1'
                            },
                            {
                                'name': 'CLK-N13 Slew Rate',
                                'key': 'pll1slew',
                                'type': 'list',
                                'values': ['+3nS', '+2nS', '+1nS', '+0nS'],
                                'default': '+0nS',
                                'set': partial(self.pll.pll_outslew_set,
                                               outnum=1),
                                'get': partial(self.pll.pll_outslew_get,
                                               outnum=1),
                                'psync': False
                            },
                            {
                                'name': 'PLL1 Frequency',
                                'key': 'pll1freq',
                                'type': 'float',
                                'limits': (0.625E6, 167E6),
                                'default': 0,
                                'step': 1E6,
                                'siPrefix': True,
                                'suffix': 'Hz',
                                'set': partial(self.pll.pll_outfreq_set,
                                               outnum=1),
                                'get': partial(self.pll.pll_outfreq_get,
                                               outnum=1),
                                'psync': False
                            },
                        ]
                    },
                    {
                        'name':
                        'CLK-E12 (FGPA Pin E12)',
                        'key':
                        'pll2',
                        'type':
                        'group',
                        'children': [
                            {
                                'name':
                                'CLK-E12 Enabled',
                                'key':
                                'pll2enabled',
                                'type':
                                'bool',
                                'default':
                                False,
                                'set':
                                partial(self.pll.pll_outenable_set, outnum=2),
                                'get':
                                partial(self.pll.pll_outenable_get, outnum=2),
                                'psync':
                                False
                            },
                            {
                                'name': 'CLK-E12 Source',
                                'key': 'pll2source',
                                'type': 'list',
                                'values': ['PLL2'],
                                'value': 'PLL2'
                            },
                            {
                                'name': 'CLK-E12 Slew Rate',
                                'key': 'pll2slew',
                                'type': 'list',
                                'values': ['+0nS', '+1nS', '+2nS', '+3nS'],
                                'default': '+0nS',
                                'set': partial(self.pll.pll_outslew_set,
                                               outnum=2),
                                'get': partial(self.pll.pll_outslew_get,
                                               outnum=2),
                                'psync': False
                            },
                            {
                                'name': 'PLL2 Frequency',
                                'key': 'pll2freq',
                                'type': 'float',
                                'limits': (0.625E6, 167E6),
                                'default': 0,
                                'step': 1E6,
                                'siPrefix': True,
                                'suffix': 'Hz',
                                'set': partial(self.pll.pll_outfreq_set,
                                               outnum=2),
                                'get': partial(self.pll.pll_outfreq_get,
                                               outnum=2),
                                'psync': False
                            },
                        ]
                    },
                    {
                        'name': 'Save as Default (stored in EEPROM)',
                        'type': 'action',
                        'action': lambda _: self.pll.pll_writedefaults()
                    },
                ]
            },
            {
                'name': 'Disable CLKUSB For Capture',
                'key': 'clkusbautooff',
                'type': 'bool',
                'value': True
            },
            {
                'name': 'Time CLKUSB Disabled for',
                'key': 'clksleeptime',
                'type': 'int',
                'range': (1, 50000),
                'value': 50,
                'suffix': 'mS'
            },
            {
                'name': 'CLKUSB Manual Setting',
                'key': 'clkusboff',
                'type': 'bool',
                'value': True,
                'action': self.usb_clk_setenabled_action
            },
            {
                'name': 'Send Trigger',
                'type': 'action',
                'action': self.usb_trigger_toggle
            },
            {
                'name': 'VCC-INT',
                'key': 'vccint',
                'type': 'float',
                'default': 1.00,
                'range': (0.6, 1.10),
                'suffix': ' V',
                'decimals': 3,
                'set': self.vccint_set,
                'get': self.vccint_get
            },
            {
                'name':
                'FPGA Bitstream',
                'type':
                'group',
                'children': [
                    {
                        'name': 'Bitstream File',
                        'key': 'fpgabsfile',
                        'type': 'file',
                        'value': "",
                        "filter": '*.bit'
                    },
                    {
                        'name': 'Program FPGA',
                        'type': 'action',
                        'action': self.gui_programfpga
                    },
                ]
            },
        ])

    def fpga_write(self, addr, data):
        """ Write to specified address """

        if addr < self._woffset:
            raise IOError("Write to read-only location: 0x%04x" % addr)

        return self._naeusb.cmdWriteMem(addr, data)

    def fpga_read(self, addr, readlen):
        """ Read from address """

        if addr > self._woffset:
            logging.info(
                'Read from write address, confirm this is not an error')

        data = self._naeusb.cmdReadMem(addr, readlen)
        return data

    def usb_clk_setenabled_action(self, p):
        self.usb_clk_setenabled(p.getValue())

    def usb_clk_setenabled(self, status):
        """ Turn on or off the Data Clock to the FPGA """
        if status:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_CLKON)
        else:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG,
                                  CW305_USB.SYSCFG_CLKOFF)

    def usb_trigger_toggle(self, _=None):
        """ Toggle the trigger line high then low """
        self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_TOGGLE)

    @setupSetParam("VCC-INT")
    def vccint_set(self, vccint=1.0):
        """ Set the VCC-INT for the FPGA """

        # print "vccint = " + str(vccint)

        if (vccint < 0.6) or (vccint > 1.15):
            raise ValueError("VCC-Int out of range 0.6V-1.1V")

        # Convert to mV
        vccint = int(vccint * 1000)
        vccsetting = [vccint & 0xff, (vccint >> 8) & 0xff, 0]

        # calculate checksum
        vccsetting[2] = vccsetting[0] ^ vccsetting[1] ^ CW305_USB.VCCINT_XORKEY

        self._naeusb.sendCtrl(CW305_USB.REQ_VCCINT, 0, vccsetting)

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        if resp[0] != 2:
            raise IOError("VCC-INT Write Error, response = %d" % resp[0])

    def vccint_get(self):
        """ Get the last set value for VCC-INT """

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        return float(resp[1] | (resp[2] << 8)) / 1000.0

    def gui_programfpga(self, _=None):
        bsfile = self.params.getChild(['FPGA Bitstream',
                                       "fpgabsfile"]).getValue()
        if not os.path.isfile(bsfile):
            raise Warning("FPGA Bitstream not configured or %s not a file." %
                          str(bsfile))
        starttime = datetime.now()
        result = self.fpga.FPGAProgram(open(bsfile, "rb"),
                                       exceptOnDoneFailure=False)
        stoptime = datetime.now()

        if result:
            logging.info('FPGA Config OK, time: %s' %
                         str(stoptime - starttime))
        else:
            logging.warning(
                'FPGA Config failed: DONE pin did not go high. Check bitstream is for target device.'
            )

    def _con(self, scope=None, bsfile=None, force=False):
        """Connect to CW305 board, download bitstream"""

        self._naeusb.con(idProduct=[0xC305])
        if self.fpga.isFPGAProgrammed() == False or force:
            if bsfile is None:
                bsfile = self.params.getChild(['FPGA Bitstream',
                                               "fpgabsfile"]).getValue()
            if not os.path.isfile(bsfile):
                print("FPGA Bitstream not configured or '%s' not a file." %
                      str(bsfile))
            else:
                from datetime import datetime
                starttime = datetime.now()
                status = self.fpga.FPGAProgram(open(bsfile, "rb"),
                                               exceptOnDoneFailure=False)
                stoptime = datetime.now()
                if status:
                    logging.info('FPGA Config OK, time: %s' %
                                 str(stoptime - starttime))
                else:
                    logging.warning(
                        'FPGA Done pin failed to go high, check bitstream is for target device.'
                    )
        self.usb_clk_setenabled(True)
        self.fpga_write(0x100 + self._woffset, [0])
        self.params.refreshAllParameters()
        self.pll.cdce906init()

    def _dis(self):
        if self._naeusb:
            self._naeusb.close()

    def checkEncryptionKey(self, key):
        """Validate encryption key"""
        return key

    def loadEncryptionKey(self, key):
        """Write encryption key to FPGA"""
        self.key = key
        key = key[::-1]
        self.fpga_write(0x100 + self._woffset, key)

    def loadInput(self, inputtext):
        """Write input to FPGA"""
        self.input = inputtext
        text = inputtext[::-1]
        self.fpga_write(0x200 + self._woffset, text)

    def isDone(self):
        """Check if FPGA is done"""
        result = self.fpga_read(0x50, 1)[0]

        if result == 0x00:
            return False
        else:
            # Clear trigger
            self.fpga_write(0x40 + self._woffset, [0])
            # LED Off
            self.fpga_write(0x10 + self._woffset, [0])
            return True

    def readOutput(self):
        """"Read output from FPGA"""
        data = self.fpga_read(0x200, 16)
        data = data[::-1]
        self.newInputData.emit(util.list2hexstr(data))
        return data

    def go(self):
        """Disable USB clock (if requested), perform encryption, re-enable clock"""
        if self.findParam('clkusbautooff').getValue():
            self.usb_clk_setenabled(False)

        #LED On
        self.fpga_write(0x10 + self._woffset, [0x01])

        time.sleep(0.01)
        self.usb_trigger_toggle()
        # self.FPGAWrite(0x100, [1])
        # self.FPGAWrite(0x100, [0])

        if self.findParam('clkusbautooff').getValue():
            time.sleep(self.findParam('clksleeptime').getValue() / 1000.0)
            self.usb_clk_setenabled(True)
Пример #2
0
class CWNano(ScopeTemplate, util.DisableNewAttr):
    """CWNano scope object.

    This class contains the public API for the CWNano hardware. It includes
    specific settings for each of these devices.

    To connect to one of these devices, the easiest method is::

        import chipwhisperer as cw
        scope = cw.scope(type=scopes.CWNano)

    Some sane default settings can be set using::

        scope.default_setup()

    For more help about scope settings, try help() on each of the ChipWhisperer
    scope submodules (scope.adc, scope.io, scope.glitch):

      * :attr:`scope.adc <.CWNano.adc>`
      * :attr:`scope.io <.CWNano.io>`
      * :attr:`scope.glitch <.CWNano.glitch>`
      * :meth:`scope.default_setup <.CWNano.default_setup>`
      * :meth:`scope.con <.CWNano.con>`
      * :meth:`scope.dis <.CWNano.dis>`
      * :meth:`scope.get_last_trace <.CWNano.get_last_trace>`
      * :meth:`scope.arm <.CWNano.arm>`
      * :meth:`scope.capture <.CWNano.capture>`
    """

    _name = "ChipWhisperer Nano"

    REQ_ARM = 0x29
    REQ_SAMPLES = 0x2A

    def __init__(self):
        ScopeTemplate.__init__(self)
        self._is_connected = False


        self._cwusb = NAEUSB()
        self.ser = self._cwusb
        self.scopetype = self
        self.dev = self

        self.xmega = XMEGAPDI(self._cwusb)
        self.avr = AVRISP(self._cwusb)
        self.usart = USART(self._cwusb)
        self.serialstm32f = STM32FSerial(cwserial=self.usart)
        self.serialstm32f.scope = self
        self.io = GPIOSettings(self._cwusb)
        self.adc = ADCSettings(self._cwusb)
        self.glitch = GlitchSettings(self._cwusb)
        self._timeout = 2

        self._lasttrace = None

        self.disable_newattr()

    def default_setup(self):
        """ Sets up sane capture defaults for this scope

          * 7.5MHz ADC clock
          * 7.5MHz output clock
          * 5000 capture samples
          * tio1 = serial rx
          * tio2 = serial tx
          * glitch module off

        .. versionadded:: 5.1
            Added default setup for CWNano
        """
        self.adc.clk_freq = 7.5E6
        self.io.clkout = 7.5E6
        self.adc.samples = 5000
        self.io.tio1 = "serial_rx"
        self.io.tio2 = "serial_tx"
        self.glitch.repeat = 0

    def getCurrentScope(self):
        return self

    def _getNAEUSB(self):
        return self._cwusb

    @property
    def sn(self):
        return self._cwusb.snum

    @property
    def latest_fw(self):
        from chipwhisperer.hardware.firmware.cwnano import fwver
        return {"major": fwver[0], "minor": fwver[1]}

    @property
    def fw_version(self):
        a = self._cwusb.readFwVersion()
        return {"major": a[0], "minor": a[1], "debug": a[2]}

    def _con(self, sn=None):
        try:
            possible_sn = self._cwusb.get_possible_devices(idProduct=[0xACE0])
            serial_numbers = []
            if len(possible_sn) > 1:
                if sn is None:
                    for d in possible_sn:
                        serial_numbers.append("sn = {} ({})".format(str(d['sn']), str(d['product'])))
                    raise Warning("Multiple ChipWhisperers detected. Please specify device from the following list using cw.scope(sn=<SN>): \n{}".format(serial_numbers))
            else:
                sn = None
            found_id = self._cwusb.con(idProduct=[0xACE0], serial_number=sn)
        except (IOError, ValueError):
            raise Warning("Could not connect to cwnano. It may have been disconnected, is in an error state, or is being used by another tool.")
        self.disable_newattr()
        self._is_connected = True
        return True

    def _dis(self):
        self.enable_newattr()
        self.usbdev().close()
        self._is_connected = False
        return True

    def arm(self):
        """Arm the ADC, the trigger will be GPIO4 rising edge (fixed trigger)."""
        with DelayedKeyboardInterrupt():
            if self.connectStatus is False:
                raise Warning("Scope \"" + self.getName() + "\" is not connected. Connect it first...")

            self._cwusb.sendCtrl(self.REQ_ARM, 1)


    def capture(self):
        """Raises IOError if unknown failure, returns 'True' if timeout, 'False' if no timeout"""

        with DelayedKeyboardInterrupt():
            starttime = datetime.datetime.now()
            while self._cwusb.readCtrl(self.REQ_ARM, dlen=1)[0] == 0:
                # Wait for a moment before re-running the loop
                time.sleep(0.001)
                diff = datetime.datetime.now() - starttime

                # If we've timed out, don't wait any longer for a trigger
                if (diff.total_seconds() > self._timeout):
                    logging.warning('Timeout in cwnano capture()')
                    return True

            self._lasttrace = self._cwusb.cmdReadMem(0, self.adc.samples)

            # can just keep rerunning this until it works I think
            i = 0
            while len(self._lasttrace) < self.adc.samples:
                logging.debug("couldn't read ADC data from Nano, retrying...")

                self._lasttrace = self._cwusb.cmdReadMem(0, self.adc.samples)
                i+= 1
                if i > 20:
                    logging.warning("Couldn't read trace data back from Nano")
                    return True
            self._lasttrace = np.array(self._lasttrace) / 256.0 - 0.5

            #self.newDataReceived(0, self._lasttrace, 0, self.adc.clk_freq)

            return False


    def get_last_trace(self):
        """Return the last trace captured with this scope.
        """
        return self._lasttrace

    getLastTrace = camel_case_deprecated(get_last_trace)


    def _dict_repr(self):
        dict = OrderedDict()
        dict['fw_version'] = self.fw_version
        dict['io']    = self.io._dict_repr()
        dict['adc']   = self.adc._dict_repr()
        dict['glitch'] = self.glitch._dict_repr()
        return dict

    def __repr__(self):
        # Add some extra information about ChipWhisperer type here
        if self._is_connected:
            ret = "ChipWhisperer Nano Device\n"
            return ret + dict_to_str(self._dict_repr())
        else:
            ret = "ChipWhisperer Nano device (disconnected)"
            return ret

    def __str__(self):
        return self.__repr__()

    def get_possible_devices(self, idProduct):
        return self._cwusb.get_possible_devices(idProduct=idProduct)

    def usbdev(self):
        return self._cwusb
Пример #3
0
class CW305(TargetTemplate):
    _name = "ChipWhisperer CW305 (Artix-7)"

    def __init__(self, parentParam=None):
        TargetTemplate.__init__(self, parentParam)
        self._naeusb = NAEUSB()
        self.pll = PLLCDCE906(self._naeusb, ref_freq = 12.0E6, parent=self)
        self.fpga = FPGA(self._naeusb)

        self.hw = None
        # self._fpgabs = QSettings().value("cw305-bitstream", '')
        self.oa = None

        self._woffset = 0x400

        self.params.addChildren([
            {'name':'PLL Settings', 'key':'pll', 'type':'group', 'children':[
                {'name':'Enabled', 'key':'pllenabled', 'type':'bool', 'default':False, 'set':self.pll.pll_enable_set, 'get':self.pll.pll_enable_get, 'psync':False},
                {'name':'CLK-SMA (X6)', 'key':'pll0', 'type':'group', 'children':[
                    {'name':'CLK-SMA Enabled', 'key':'pll0enabled', 'type':'bool', 'default':False, 'set':partial(self.pll.pll_outenable_set, outnum=0), 'get':partial(self.pll.pll_outenable_get, outnum=0), 'psync':False},
                    {'name':'CLK-SMA Source', 'key':'pll0source', 'type':'list', 'values':['PLL0', 'PLL1', 'PLL2'], 'default':'PLL0', 'set':partial(self.pll.pll_outsource_set, outnum=0), 'get':partial(self.pll.pll_outsource_get, outnum=0), 'psync':False},
                    {'name':'CLK-SMA Slew Rate', 'key':'pll0slew', 'type':'list', 'values':['+3nS', '+2nS', '+1nS', '+0nS'], 'default':'+0nS', 'set':partial(self.pll.pll_outslew_set, outnum=0), 'get':partial(self.pll.pll_outslew_get, outnum=0), 'psync':False},
                    {'name':'PLL0 Frequency', 'key':'pll0freq', 'type':'float', 'limits':(0.625E6, 167E6), 'default':0, 'step':1E6,
                        'siPrefix':True, 'suffix':'Hz', 'set':partial(self.pll.pll_outfreq_set, outnum=0), 'get':partial(self.pll.pll_outfreq_get, outnum=0), 'psync':False},
                ]},
                {'name':'CLK-N13 (FGPA Pin N13)', 'key':'pll1', 'type':'group', 'children':[
                    {'name':'CLK-N13 Enabled', 'key':'pll1enabled', 'type':'bool', 'default':False, 'set':partial(self.pll.pll_outenable_set, outnum=1), 'get':partial(self.pll.pll_outenable_get, outnum=1), 'psync':False},
                    {'name':'CLK-N13 Source', 'key':'pll1source', 'type':'list', 'values':['PLL1'], 'value':'PLL1'},
                    {'name':'CLK-N13 Slew Rate', 'key':'pll1slew', 'type':'list', 'values':['+3nS', '+2nS', '+1nS', '+0nS'], 'default':'+0nS', 'set':partial(self.pll.pll_outslew_set, outnum=1), 'get':partial(self.pll.pll_outslew_get, outnum=1), 'psync':False},
                    {'name':'PLL1 Frequency', 'key':'pll1freq', 'type':'float', 'limits':(0.625E6, 167E6), 'default':0, 'step':1E6,
                        'siPrefix':True, 'suffix':'Hz', 'set':partial(self.pll.pll_outfreq_set, outnum=1), 'get':partial(self.pll.pll_outfreq_get, outnum=1), 'psync':False},
                ]},
                {'name':'CLK-E12 (FGPA Pin E12)', 'key':'pll2', 'type':'group', 'children':[
                    {'name':'CLK-E12 Enabled', 'key':'pll2enabled', 'type':'bool', 'default':False, 'set':partial(self.pll.pll_outenable_set, outnum=2), 'get':partial(self.pll.pll_outenable_get, outnum=2), 'psync':False},
                    {'name':'CLK-E12 Source', 'key':'pll2source', 'type':'list', 'values':['PLL2'], 'value':'PLL2'},
                    {'name':'CLK-E12 Slew Rate', 'key':'pll2slew', 'type':'list', 'values':['+0nS', '+1nS', '+2nS', '+3nS'], 'default':'+0nS', 'set':partial(self.pll.pll_outslew_set, outnum=2), 'get':partial(self.pll.pll_outslew_get, outnum=2), 'psync':False},
                    {'name':'PLL2 Frequency', 'key':'pll2freq', 'type':'float', 'limits':(0.625E6, 167E6), 'default':0, 'step':1E6,
                        'siPrefix':True, 'suffix':'Hz', 'set':partial(self.pll.pll_outfreq_set, outnum=2), 'get':partial(self.pll.pll_outfreq_get, outnum=2), 'psync':False},
                ]},
                {'name':'Save as Default (stored in EEPROM)', 'type':'action', 'action':lambda _ : self.pll.pll_writedefaults()},
            ]},
            {'name':'Disable CLKUSB For Capture', 'key':'clkusbautooff', 'type':'bool', 'value':True},
            {'name':'Time CLKUSB Disabled for', 'key':'clksleeptime', 'type':'int', 'range':(1, 50000), 'value':50, 'suffix':'mS'},
            {'name':'CLKUSB Manual Setting', 'key':'clkusboff', 'type':'bool', 'value':True, 'action':self.usb_clk_setenabled_action},
            {'name':'Send Trigger', 'type':'action', 'action':self.usb_trigger_toggle},
            {'name':'VCC-INT', 'key':'vccint', 'type':'float', 'default':1.00, 'range':(0.6, 1.10), 'suffix':' V', 'decimals':3, 'set':self.vccint_set, 'get':self.vccint_get},
            {'name':'FPGA Bitstream', 'type':'group', 'children':[
                    {'name':'Bitstream File', 'key':'fpgabsfile', 'type':'file', 'value':"", "filter":'*.bit'},
                    {'name':'Program FPGA', 'type':'action', 'action':self.gui_programfpga},
            ]},
        ])

    def fpga_write(self, addr, data):
        """ Write to specified address """

        if addr < self._woffset:
            raise IOError("Write to read-only location: 0x%04x"%addr)

        return self._naeusb.cmdWriteMem(addr, data)
        
    def fpga_read(self, addr, readlen):
        """ Read from address """

        if addr > self._woffset:
            print "NOTE: Read from write address, confirm this is not an error"

        data = self._naeusb.cmdReadMem(addr, readlen)
        return data

    def usb_clk_setenabled_action(self, p):
        self.usb_clk_setenabled(p.getValue())

    def usb_clk_setenabled(self, status):
        """ Turn on or off the Data Clock to the FPGA """
        if status:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_CLKON)
        else:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_CLKOFF)

    def usb_trigger_toggle(self, _=None):
        """ Toggle the trigger line high then low """
        self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_TOGGLE)
        
    @setupSetParam("VCC-INT")
    def vccint_set(self, vccint=1.0):
        """ Set the VCC-INT for the FPGA """

        # print "vccint = " + str(vccint)

        if (vccint < 0.6) or (vccint > 1.15):
            raise ValueError("VCC-Int out of range 0.6V-1.1V")
        
        # Convert to mV
        vccint = int(vccint * 1000)
        vccsetting = [vccint & 0xff, (vccint >> 8) & 0xff, 0]

        # calculate checksum
        vccsetting[2] = vccsetting[0] ^ vccsetting[1] ^ CW305_USB.VCCINT_XORKEY

        self._naeusb.sendCtrl(CW305_USB.REQ_VCCINT, 0, vccsetting)

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        if resp[0] != 2:
            raise IOError("VCC-INT Write Error, response = %d" % resp[0])

    def vccint_get(self):
        """ Get the last set value for VCC-INT """

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        return float(resp[1] | (resp[2] << 8)) / 1000.0

    def gui_programfpga(self, _=None):
        bsfile = self.params.getChild(['FPGA Bitstream',"fpgabsfile"]).getValue()
        if not os.path.isfile(bsfile):
            raise Warning("FPGA Bitstream not configured or %s not a file." % str(bsfile))
        starttime = datetime.now()
        self.fpga.FPGAProgram(open(bsfile, "rb"))
        stoptime = datetime.now()
        print "FPGA Config time: %s" % str(stoptime - starttime)

    def con(self, scope = None, bsfile=None, force = False):
        """Connect to CW305 board, download bitstream"""

        self._naeusb.con(idProduct=0xC305)
        if self.fpga.isFPGAProgrammed() == False or force:
            bsfile = self.params.getChild(['FPGA Bitstream',"fpgabsfile"]).getValue()
            if not os.path.isfile(bsfile):
                print("FPGA Bitstream not configured or %s not a file." % str(bsfile))
            else:
                from datetime import datetime
                starttime = datetime.now()
                self.fpga.FPGAProgram(open(bsfile, "rb"))
                stoptime = datetime.now()
                print "FPGA Config time: %s" % str(stoptime - starttime)
        self.usb_clk_setenabled(True)
        self.fpga_write(0x100+self._woffset, [0])
        self.params.refreshAllParameters()
        self.pll.cdce906init()
        self.connectStatus.setValue(True)

    def checkEncryptionKey(self, key):
        """Validate encryption key"""
        return key 

    def loadEncryptionKey(self, key):
        """Write encryption key to FPGA"""
        self.key = key
        key = key[::-1]
        self.fpga_write(0x100+self._woffset, key)

    def loadInput(self, inputtext):
        """Write input to FPGA"""
        self.input = inputtext
        text = inputtext[::-1]
        self.fpga_write(0x200+self._woffset, text)

    def isDone(self):
        """Check if FPGA is done"""
        result = self.fpga_read(0x50, 1)[0]

        if result == 0x00:
            return False
        else:
            # Clear trigger
            self.fpga_write(0x40+self._woffset, [0])
            # LED Off
            self.fpga_write(0x10+self._woffset, [0])
            return True
        
    def readOutput(self):
        """"Read output from FPGA"""
        data = self.fpga_read(0x200, 16)
        data = data[::-1]
        return data

    def go(self):
        """Disable USB clock (if requested), perform encryption, re-enable clock"""
        if self.findParam('clkusbautooff').getValue():
            self.usb_clk_setenabled(False)
            
        #LED On
        self.fpga_write(0x10+self._woffset, [0x01])
            

        time.sleep(0.01)
        self.usb_trigger_toggle()
        # self.FPGAWrite(0x100, [1])
        # self.FPGAWrite(0x100, [0])

        if self.findParam('clkusbautooff').getValue():
            time.sleep(self.findParam('clksleeptime').getValue() / 1000.0)
            self.usb_clk_setenabled(True)
Пример #4
0
class CW305(TargetTemplate):
    """CW305 target object.

    This class contains the public API for the CW305 hardware.
    To connect to the CW305, the easiest method is::

        import chipwhisperer as cw
        scope = cw.scope()

        # scope can also be None here, unlike with the default SimpleSerial
        target = cw.target(scope,
                targets.CW305, bsfile=<valid FPGA bitstream file>)

    As of CW5.3, you can also specify the following::

        # can also be '35t'
        target = cw.target(scope, fpga_id='100t')

    To automatically program with the example AES bitstream

    If you're using the reference designs, typical configuration
    requires you to set the FPGA VCC-INT voltage and enable and 
    set the clock via the PLL. You'll probably also want to
    disable the USB clock during encryption to reduce noise::

        target.vccint_set(1.0) #set VCC-INT to 1V
        
        target.pll.pll_enable_set(True) #Enable PLL chip
        target.pll.pll_outenable_set(False, 0) # Disable unused PLL0
        target.pll.pll_outenable_set(True, 1)  # Enable PLL 
        target.pll.pll_outenable_set(False, 2) # Disable unused PLL2

        # optional, but reduces power trace noise
        target.clkusbautooff = True
        target.clksleeptime = 1 # 1ms typically good for sleep


    Don't forget to clock the ChipWhisperer ADC off the FPGA instead
    of the internal clock::

        scope.clock.adc_src = "extclk_x4"
        scope.clock.reset_adc() # make sure the DCM is locked

    Note that connecting to the CW305 includes programming the CW305 FPGA,
    if it isn't already.

    For more help about CW305 settings, try help() on this CW305 submodule:

       * target.pll
    """

    _name = "ChipWhisperer CW305 (Artix-7)"
    BATCHRUN_START = 0x1
    BATCHRUN_RANDOM_KEY = 0x2
    BATCHRUN_RANDOM_PT = 0x4

    def __init__(self):
        TargetTemplate.__init__(self)
        self._naeusb = NAEUSB()
        self.pll = PLLCDCE906(self._naeusb, ref_freq=12.0E6)
        self.fpga = FPGA(self._naeusb)

        self.hw = None
        self.oa = None

        self._woffset = 0x400
        self._woffset_sam3U = 0x000

        self._clksleeptime = 1
        self._clkusbautooff = True
        self.last_key = bytearray([0] * 16)

    def _getNAEUSB(self):
        return self._naeusb

    def fpga_write(self, addr, data):
        """Write to an address on the FPGA

        Args:
            addr (int): Address to write to
            data (list): Data to write to addr

        Raises:
            IOError: User attempted to write to a read-only location
        """
        if addr < self._woffset:
            raise IOError("Write to read-only location: 0x%04x" % addr)

        return self._naeusb.cmdWriteMem(addr, data)

    def fpga_read(self, addr, readlen):
        """Read from an address on the FPGA

        Args:
            addr (int): Address to read from
            readlen (int): Length of data to read

        Returns:
            Requested data as a list
        """
        if addr > self._woffset:
            logging.info(
                'Read from write address, confirm this is not an error')

        data = self._naeusb.cmdReadMem(addr, readlen)
        return data

    def usb_clk_setenabled(self, status):
        """ Turn on or off the Data Clock to the FPGA """
        if status:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_CLKON)
        else:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG,
                                  CW305_USB.SYSCFG_CLKOFF)

    def usb_trigger_toggle(self, _=None):
        """ Toggle the trigger line high then low """
        self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_TOGGLE)

    def vccint_set(self, vccint=1.0):
        """ Set the VCC-INT for the FPGA """

        # print "vccint = " + str(vccint)

        if (vccint < 0.6) or (vccint > 1.15):
            raise ValueError("VCC-Int out of range 0.6V-1.1V")

        # Convert to mV
        vccint = int(vccint * 1000)
        vccsetting = [vccint & 0xff, (vccint >> 8) & 0xff, 0]

        # calculate checksum
        vccsetting[2] = vccsetting[0] ^ vccsetting[1] ^ CW305_USB.VCCINT_XORKEY

        self._naeusb.sendCtrl(CW305_USB.REQ_VCCINT, 0, vccsetting)

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        if resp[0] != 2:
            raise IOError("VCC-INT Write Error, response = %d" % resp[0])

    def vccint_get(self):
        """ Get the last set value for VCC-INT """

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        return float(resp[1] | (resp[2] << 8)) / 1000.0

    def _con(self, scope=None, bsfile=None, force=False, fpga_id=None):
        """Connect to CW305 board, and download bitstream.

        If the target has already been programmed it skips reprogramming
        unless forced.

        Args:
            scope (ScopeTemplate): An instance of a scope object.
            bsfile (path): The path to the bitstream file to program the FPGA with.
            force (bool): Whether or not to force reprogramming.
            fpga_id (string): '100t', '35t', or None. If bsfile is None and fpga_id specified,
                              program with AES firmware for fpga_id
        """

        from datetime import datetime
        self._naeusb.con(idProduct=[0xC305])
        if not fpga_id is None:
            if fpga_id not in ('100t', '35t'):
                raise ValueError(f"Invalid fpga {fpga_id}")
        self._fpga_id = fpga_id
        if self.fpga.isFPGAProgrammed() == False or force:
            if bsfile is None:
                if not fpga_id is None:
                    from chipwhisperer.hardware.firmware.cw305 import getsome
                    bsdata = getsome(f"AES_{fpga_id}.bit")
                    starttime = datetime.now()
                    status = self.fpga.FPGAProgram(bsdata,
                                                   exceptOnDoneFailure=False)
                    stoptime = datetime.now()
                    if status:
                        logging.info('FPGA Config OK, time: %s' %
                                     str(stoptime - starttime))
                    else:
                        logging.warning(
                            'FPGA Done pin failed to go high, check bitstream is for target device.'
                        )
                else:
                    print("No FPGA Bitstream file specified.")
            elif not os.path.isfile(bsfile):
                print(("FPGA Bitstream not configured or '%s' not a file." %
                       str(bsfile)))
            else:
                starttime = datetime.now()
                status = self.fpga.FPGAProgram(open(bsfile, "rb"),
                                               exceptOnDoneFailure=False)
                stoptime = datetime.now()
                if status:
                    logging.info('FPGA Config OK, time: %s' %
                                 str(stoptime - starttime))
                else:
                    logging.warning(
                        'FPGA Done pin failed to go high, check bitstream is for target device.'
                    )
        self.usb_clk_setenabled(True)
        self.fpga_write(0x100 + self._woffset, [0])
        self.pll.cdce906init()

    def _dis(self):
        if self._naeusb:
            self._naeusb.close()

    def checkEncryptionKey(self, key):
        """Validate encryption key"""
        return key

    def loadEncryptionKey(self, key):
        """Write encryption key to FPGA"""
        self.key = key
        key = key[::-1]
        self.fpga_write(0x100 + self._woffset, key)

    def loadInput(self, inputtext):
        """Write input to FPGA"""
        self.input = inputtext
        text = inputtext[::-1]
        self.fpga_write(0x200 + self._woffset, text)

    def is_done(self):
        """Check if FPGA is done"""
        result = self.fpga_read(0x50, 1)[0]

        if result == 0x00:
            return False
        else:
            # Clear trigger
            self.fpga_write(0x40 + self._woffset, [0])
            # LED Off
            self.fpga_write(0x10 + self._woffset, [0])
            return True

    isDone = camel_case_deprecated(is_done)

    def readOutput(self):
        """"Read output from FPGA"""
        data = self.fpga_read(0x200, 16)
        data = data[::-1]
        #self.newInputData.emit(util.list2hexstr(data))
        return data

    @property
    def latest_fw(self):
        cw_type = self._getCWType()
        if cw_type == "cwlite":
            from chipwhisperer.hardware.firmware.cwlite import fwver
        elif cw_type == "cw1200":
            from chipwhisperer.hardware.firmware.cw1200 import fwver

        ret = OrderedDict()
        return {"major": fwver[0], "minor": fwver[1]}

    @property
    def fw_version(self):
        a = self._naeusb.readFwVersion()
        return {"major": a[0], "minor": a[1], "debug": a[2]}

    @property
    def clkusbautooff(self):
        """ If set, the USB clock is automatically disabled on capture.

        The USB clock is re-enabled after self.clksleeptime milliseconds.

        Reads/Writes to the FPGA will not be possible until after
        the USB clock is reenabled, meaning :code:`usb_trigger_toggle()`
        must be used to trigger the FPGA to perform an encryption.

        :Getter: Gets whether to turn off the USB clock on capture

        :Setter: Sets whether to turn off the USB clock on capture
        """
        return self._clkusbautooff

    @clkusbautooff.setter
    def clkusbautooff(self, state):
        self._clkusbautooff = state

    @property
    def clksleeptime(self):
        """ Time (in milliseconds) that the USB clock is disabled for upon
        capture, if self.clkusbautooff is set.
        """
        return self._clksleeptime

    @clksleeptime.setter
    def clksleeptime(self, value):
        self._clksleeptime = value

    def go(self):
        """Disable USB clock (if requested), perform encryption, re-enable clock"""
        if self.clkusbautooff:
            self.usb_clk_setenabled(False)

        #LED On
        self.fpga_write(0x10 + self._woffset, [0x01])

        time.sleep(0.001)
        self.usb_trigger_toggle()
        # self.FPGAWrite(0x100, [1])
        # self.FPGAWrite(0x100, [0])

        if self.clkusbautooff:
            time.sleep(self.clksleeptime / 1000.0)
            self.usb_clk_setenabled(True)

    def simpleserial_read(self, cmd, pay_len, end='\n', timeout=250, ack=True):
        """Read data from target

        Mimics simpleserial protocol of serial based targets

        Args:
            cmd (str): Command to ues. Only accepts 'r' for now.
            pay_len: Unused
            end: Unused
            timeout: Unused
            ack: Unused

        Returns: Value from Crypto output register

        .. versionadded:: 5.1
            Added simpleserial_read to CW305
        """
        if cmd == "r":
            return self.readOutput()
        else:
            raise ValueError("Unknown command {}".format(cmd))

    def simpleserial_write(self, cmd, data, end=None):
        """Write data to target.

        Mimics simpleserial protocol of serial based targets.

        Args:
            cmd (str): Command to use. Target supports 'p' (write plaintext),
                and 'k' (write key).
            data (bytearray): Data to write to target
            end: Unused

        Raises:
            ValueError: Unknown command

        .. versionadded:: 5.1
            Added simpleserial_write to CW305
        """
        if cmd == 'p':
            self.loadInput(data)
            self.go()
        elif cmd == 'k':
            self.loadEncryptionKey(data)
        else:
            raise ValueError("Unknown command {}".format(cmd))

    def set_key(self, key, ack=False, timeout=250):
        """Checks if key is different from the last one sent. If so, send it.

        Args:
            key (bytearray):  key to send
            ack: Unused
            timeout: Unused

        .. versionadded:: 5.1
            Added set_key to CW305
        """
        if self.last_key != key:
            self.last_key = key
            self.simpleserial_write('k', key)

    def batchRun(self,
                 batchsize=1024,
                 random_key=True,
                 random_pt=True,
                 seed=None):
        """
            Run multiple encryptions on random data

            Args:
                batchsize (int): The number of encryption to run (default 1024).
                random_key (bool): True if the key is random (default False).
                random_pt (bool): True if the plaintext are random (default True).
                seed (int): random int32 for the PRG.
        """
        if seed is None:
            seed = random.randint(0, 2**32)

        data = []
        data.extend(
            packuint32(1 | (random_key << 1) | (random_pt << 2)
                       | (batchsize << 16)))
        data.extend(packuint32(seed))
        self.sam3u_write(0, data)

        # generate the inputs
        if random_key:
            key = [[0 for x in range(16)] for y in range(batchsize)]
        else:
            key = None

        if random_pt:
            pt = [[0 for x in range(16)] for y in range(batchsize)]
        else:
            pt = None

        for b in range(batchsize):
            if random_key:
                for j in range(16):
                    key[b][15 - j] = seed >> 24
                    seed += ((seed * seed) & 0xffffffff) | 0x5
                    seed &= 0xffffffff
            if random_pt:
                for j in range(16):
                    pt[b][15 - j] = seed >> 24
                    seed += ((seed * seed) & 0xffffffff) | 0x5
                    seed &= 0xffffffff
        return key, pt

    def sam3u_write(self, addr, data):
        """Write to an address on the FPGA

        Args:
            addr (int): Address to write to
            data (list): Data to write to addr

        Raises:
            IOError: User attempted to write to a read-only location
        """
        if addr < self._woffset_sam3U:
            raise IOError("Write to read-only location: 0x%04x" % addr)
        if len(data) > (256 + addr):
            raise IOError("Write will overflow at location: 0x%04x" % (256))

        return self._naeusb.cmdWriteSam3U(addr, data)

    @fw_ver_required(0, 30)
    def spi_mode(self, enable=True, timeout=200, bsfile=None):
        """Enter programming mode for the onboard SPI chip
        
        Reprograms the FPGA with the appropriate bitstream and 
        returns an object with which to program the CW305 SPI chip
        (see documentation on the returned object for more info)

        Args:
            enable (bool): Enable the SPI interface before returning it. Defaults to True
            timeout (int): USB timeout in ms. Defaults to 200.
            bsfile (string): If not None, program with a bitstream pointed to by bsfile.
                             If None, program with SPI passthrough bitstream for the chip
                             specified during connection (or cw.target()) 

        Returns:
            A FPGASPI object which can be used to erase/program/verify/read the SPI
            chip on the CW305.
        """
        from datetime import datetime
        if self._fpga_id is None and bsfile is None:
            logging.warning(
                "CW305 requires passthrough bitstream to program SPI chip, but file/chip not specified"
            )
        else:
            bsdata = None
            if self._fpga_id:
                from chipwhisperer.hardware.firmware.cw305 import getsome
                bsdata = getsome(f"SPI_flash_{self._fpga_id}.bit")
            else:
                bsdata = open(bsfile, "rb")
            starttime = datetime.now()
            status = self.fpga.FPGAProgram(bsdata, exceptOnDoneFailure=False)
            stoptime = datetime.now()
            if status:
                logging.info('FPGA Config OK, time: %s' %
                             str(stoptime - starttime))
            else:
                logging.warning(
                    'FPGA Done pin failed to go high, check bitstream is for target device.'
                )

        spi = FPGASPI(self._naeusb, timeout)
        spi.enable_interface(enable)
        return spi
Пример #5
0
class CW305(TargetTemplate):
    """CW305 target object.

    This class contains the public API for the CW305 hardware.
    To connect to the CW305, the easiest method is::

        import chipwhisperer as cw
        scope = cw.scope()

        # scope can also be None here, unlike with the default SimpleSerial
        target = cw.target(scope,
                targets.CW305, bsfile=<valid FPGA bitstream file>)

    As of CW5.3, you can also specify the following::

        # can also be '35t'
        target = cw.target(scope, fpga_id='100t')

    To automatically program with the example AES bitstream

    If you're using the reference designs, typical configuration
    requires you to set the FPGA VCC-INT voltage and enable and 
    set the clock via the PLL. You'll probably also want to
    disable the USB clock during encryption to reduce noise::

        target.vccint_set(1.0) #set VCC-INT to 1V
        
        target.pll.pll_enable_set(True) #Enable PLL chip
        target.pll.pll_outenable_set(False, 0) # Disable unused PLL0
        target.pll.pll_outenable_set(True, 1)  # Enable PLL 
        target.pll.pll_outenable_set(False, 2) # Disable unused PLL2

        # optional, but reduces power trace noise
        target.clkusbautooff = True
        target.clksleeptime = 1 # 1ms typically good for sleep


    Don't forget to clock the ChipWhisperer ADC off the FPGA instead
    of the internal clock::

        scope.clock.adc_src = "extclk_x4"
        scope.clock.reset_adc() # make sure the DCM is locked

    Note that connecting to the CW305 includes programming the CW305 FPGA,
    if it isn't already.

    For more help about CW305 settings, try help() on this CW305 submodule:

       * target.pll
    """

    _name = "ChipWhisperer CW305 (Artix-7)"
    BATCHRUN_START = 0x1
    BATCHRUN_RANDOM_KEY = 0x2
    BATCHRUN_RANDOM_PT = 0x4

    def __init__(self):
        TargetTemplate.__init__(self)
        self._naeusb = NAEUSB()
        self.pll = PLLCDCE906(self._naeusb, ref_freq=12.0E6)
        self.fpga = FPGA(self._naeusb)

        self.hw = None
        self.oa = None

        self._woffset_sam3U = 0x000
        self.default_verilog_defines = 'cw305_defines.v'
        self.default_verilog_defines_full_path = '../../hardware/victims/cw305_artixtarget/fpga/common/' + self.default_verilog_defines
        self.registers = 12  # number of registers we expect to find
        self.bytecount_size = 7  # pBYTECNT_SIZE in Verilog

        self._clksleeptime = 1
        self._clkusbautooff = True
        self.last_key = bytearray([0] * 16)
        self.target_name = 'AES'

    def _getNAEUSB(self):
        return self._naeusb

    def slurp_defines(self, defines_files=None):
        """ Parse Verilog defines file so we can access register and bit
        definitions by name and avoid 'magic numbers'.
        Args:
            defines_files (list): list of Verilog define files to parse
        """
        self.verilog_define_matches = 0
        if type(defines_files) != list:
            logging.error(
                'defines_files must be provided as a list (even it it contains a single element)'
            )
        for i, defines_file in enumerate(defines_files):
            if type(defines_file) == io.BytesIO:
                defines = io.TextIOWrapper(defines_file)
            else:
                if not os.path.isfile(defines_file):
                    logging.error(
                        'Cannot find %s. Please specify the location of %s on your filesystem.'
                        % (defines_files, self.default_verilog_defines))
                defines = open(defines_file, 'r')
            define_regex_base = re.compile(r'`define')
            define_regex_reg = re.compile(r'`define\s+?REG_')
            define_regex_radix = re.compile(
                r'`define\s+?(\w+).+?\'([bdh])([0-9a-fA-F]+)')
            define_regex_noradix = re.compile(r'`define\s+?(\w+?)\s+?(\d+?)')
            block_offset = 0
            for define in defines:
                if define_regex_base.search(define):
                    reg = define_regex_reg.search(define)
                    match = define_regex_radix.search(define)
                    if match:
                        self.verilog_define_matches += 1
                        if match.group(2) == 'b':
                            radix = 2
                        elif match.group(2) == 'h':
                            radix = 16
                        else:
                            radix = 10
                        setattr(self, match.group(1),
                                int(match.group(3), radix) + block_offset)
                    else:
                        match = define_regex_noradix.search(define)
                        if match:
                            self.verilog_define_matches += 1
                            setattr(self, match.group(1),
                                    int(match.group(2), 10) + block_offset)
                        else:
                            logging.warning("Couldn't parse line: %s", define)
            defines.close()
        # make sure everything is cool:
        if self.verilog_define_matches != self.registers:
            logging.warning(
                "Trouble parsing Verilog defines files (%s): didn't find the right number of defines; expected %d, got %d.\n"
                % (defines_file, self.registers, self.verilog_define_matches) +
                "Ensure that the Verilog defines files above are the same that were used to build the bitfile."
            )

    def get_fpga_buildtime(self):
        """Returns date and time when FPGA bitfile was generated.
        """
        raw = self.fpga_read(self.REG_BUILDTIME, 4)
        # definitions: Xilinx XAPP1232
        day = raw[3] >> 3
        month = ((raw[3] & 0x7) << 1) + (raw[2] >> 7)
        year = ((raw[2] >> 1) & 0x3f) + 2000
        hour = ((raw[2] & 0x1) << 4) + (raw[1] >> 4)
        minute = ((raw[1] & 0xf) << 2) + (raw[0] >> 6)
        return "FPGA build time: {}/{}/{}, {}:{}".format(
            month, day, year, hour, minute)

    def fpga_write(self, addr, data):
        """Write to an address on the FPGA

        Args:
            addr (int): Address to write to
            data (list): Data to write to addr

        """
        addr = addr << self.bytecount_size
        return self._naeusb.cmdWriteMem(addr, data)

    def fpga_read(self, addr, readlen):
        """Read from an address on the FPGA

        Args:
            addr (int): Address to read from
            readlen (int): Length of data to read

        Returns:
            Requested data as a list
        """
        addr = addr << self.bytecount_size
        data = self._naeusb.cmdReadMem(addr, readlen)
        return data

    def usb_clk_setenabled(self, status):
        """ Turn on or off the Data Clock to the FPGA """
        if status:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_CLKON)
        else:
            self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG,
                                  CW305_USB.SYSCFG_CLKOFF)

    def usb_trigger_toggle(self, _=None):
        """ Toggle the trigger line high then low """
        self._naeusb.sendCtrl(CW305_USB.REQ_SYSCFG, CW305_USB.SYSCFG_TOGGLE)

    def vccint_set(self, vccint=1.0):
        """ Set the VCC-INT for the FPGA """

        # print "vccint = " + str(vccint)

        if (vccint < 0.6) or (vccint > 1.15):
            raise ValueError("VCC-Int out of range 0.6V-1.1V")

        # Convert to mV
        vccint = int(vccint * 1000)
        vccsetting = [vccint & 0xff, (vccint >> 8) & 0xff, 0]

        # calculate checksum
        vccsetting[2] = vccsetting[0] ^ vccsetting[1] ^ CW305_USB.VCCINT_XORKEY

        self._naeusb.sendCtrl(CW305_USB.REQ_VCCINT, 0, vccsetting)

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        if resp[0] != 2:
            raise IOError("VCC-INT Write Error, response = %d" % resp[0])

    def vccint_get(self):
        """ Get the last set value for VCC-INT """

        resp = self._naeusb.readCtrl(CW305_USB.REQ_VCCINT, dlen=3)
        return float(resp[1] | (resp[2] << 8)) / 1000.0

    def _con(self,
             scope=None,
             bsfile=None,
             force=False,
             fpga_id=None,
             defines_files=None,
             slurp=True):
        """Connect to CW305 board, and download bitstream.

        If the target has already been programmed it skips reprogramming
        unless forced.

        Args:
            scope (ScopeTemplate): An instance of a scope object.
            bsfile (path): The path to the bitstream file to program the FPGA with.
            force (bool): Whether or not to force reprogramming.
            fpga_id (string): '100t', '35t', or None. If bsfile is None and fpga_id specified,
                              program with AES firmware for fpga_id
            defines_files (list, optional): path to cw305_defines.v
            slurp (bool, optional): Whether or not to slurp the Verilog defines.
        """

        from datetime import datetime
        self._naeusb.con(idProduct=[0xC305])
        if not fpga_id is None:
            if fpga_id not in ('100t', '35t'):
                raise ValueError(f"Invalid fpga {fpga_id}")
        self._fpga_id = fpga_id
        if self.fpga.isFPGAProgrammed() == False or force:
            if bsfile is None:
                if not fpga_id is None:
                    from chipwhisperer.hardware.firmware.cw305 import getsome
                    if self.target_name == 'AES':
                        bsdata = getsome(f"AES_{fpga_id}.bit")
                    elif self.target_name == 'Cryptech ecdsa256-v1 pmul':
                        bsdata = getsome(f"ECDSA256v1_pmul_{fpga_id}.bit")
                    starttime = datetime.now()
                    status = self.fpga.FPGAProgram(bsdata,
                                                   exceptOnDoneFailure=False)
                    stoptime = datetime.now()
                    if status:
                        logging.info('FPGA Config OK, time: %s' %
                                     str(stoptime - starttime))
                    else:
                        logging.warning(
                            'FPGA Done pin failed to go high, check bitstream is for target device.'
                        )
                else:
                    print("No FPGA Bitstream file specified.")
            elif not os.path.isfile(bsfile):
                print(("FPGA Bitstream not configured or '%s' not a file." %
                       str(bsfile)))
            else:
                starttime = datetime.now()
                status = self.fpga.FPGAProgram(open(bsfile, "rb"),
                                               exceptOnDoneFailure=False)
                stoptime = datetime.now()
                if status:
                    logging.info('FPGA Config OK, time: %s' %
                                 str(stoptime - starttime))
                else:
                    logging.warning(
                        'FPGA Done pin failed to go high, check bitstream is for target device.'
                    )

        self.usb_clk_setenabled(True)
        self.pll.cdce906init()

        if defines_files is None:
            if fpga_id is None:
                verilog_defines = [self.default_verilog_defines_full_path]
            else:
                from chipwhisperer.hardware.firmware.cw305 import getsome
                verilog_defines = [getsome(self.default_verilog_defines)]
        else:
            verilog_defines = defines_files
        if slurp:
            self.slurp_defines(verilog_defines)

    def _dis(self):
        if self._naeusb:
            self._naeusb.close()

    def checkEncryptionKey(self, key):
        """Validate encryption key"""
        return key

    def loadEncryptionKey(self, key):
        """Write encryption key to FPGA"""
        self.key = key
        key = key[::-1]
        self.fpga_write(self.REG_CRYPT_KEY, key)

    def loadInput(self, inputtext):
        """Write input to FPGA"""
        self.input = inputtext
        text = inputtext[::-1]
        self.fpga_write(self.REG_CRYPT_TEXTIN, text)

    def is_done(self):
        """Check if FPGA is done"""
        result = self.fpga_read(self.REG_CRYPT_GO, 1)[0]
        if result == 0x01:
            return False
        else:
            self.fpga_write(self.REG_USER_LED, [0])
            return True

    isDone = camel_case_deprecated(is_done)

    def readOutput(self):
        """"Read output from FPGA"""
        data = self.fpga_read(self.REG_CRYPT_CIPHEROUT, 16)
        data = data[::-1]
        #self.newInputData.emit(util.list2hexstr(data))
        return data

    @property
    def latest_fw(self):
        cw_type = self._getCWType()
        if cw_type == "cwlite":
            from chipwhisperer.hardware.firmware.cwlite import fwver
        elif cw_type == "cw1200":
            from chipwhisperer.hardware.firmware.cw1200 import fwver

        ret = OrderedDict()
        return {"major": fwver[0], "minor": fwver[1]}

    @property
    def fw_version(self):
        a = self._naeusb.readFwVersion()
        return {"major": a[0], "minor": a[1], "debug": a[2]}

    @property
    def clkusbautooff(self):
        """ If set, the USB clock is automatically disabled on capture.

        The USB clock is re-enabled after self.clksleeptime milliseconds.

        Reads/Writes to the FPGA will not be possible until after
        the USB clock is reenabled, meaning :code:`usb_trigger_toggle()`
        must be used to trigger the FPGA to perform an encryption.

        :Getter: Gets whether to turn off the USB clock on capture

        :Setter: Sets whether to turn off the USB clock on capture
        """
        return self._clkusbautooff

    @clkusbautooff.setter
    def clkusbautooff(self, state):
        self._clkusbautooff = state

    @property
    def clksleeptime(self):
        """ Time (in milliseconds) that the USB clock is disabled for upon
        capture, if self.clkusbautooff is set.
        """
        return self._clksleeptime

    @clksleeptime.setter
    def clksleeptime(self, value):
        self._clksleeptime = value

    def go(self):
        """Disable USB clock (if requested), perform encryption, re-enable clock"""
        if self.clkusbautooff:
            self.usb_clk_setenabled(False)

        self.fpga_write(self.REG_USER_LED, [0x01])

        time.sleep(0.001)
        self.usb_trigger_toggle()
        # it's also possible to 'go' via register write but that won't take if
        # the USB clock was turned off:
        #self.fpga_write(self.REG_CRYPT_GO, [1])

        if self.clkusbautooff:
            time.sleep(self.clksleeptime / 1000.0)
            self.usb_clk_setenabled(True)

    def simpleserial_read(self, cmd, pay_len, end='\n', timeout=250, ack=True):
        """Read data from target

        Mimics simpleserial protocol of serial based targets

        Args:
            cmd (str): Command to ues. Only accepts 'r' for now.
            pay_len: Unused
            end: Unused
            timeout: Unused
            ack: Unused

        Returns: Value from Crypto output register

        .. versionadded:: 5.1
            Added simpleserial_read to CW305
        """
        if cmd == "r":
            return self.readOutput()
        else:
            raise ValueError("Unknown command {}".format(cmd))

    def simpleserial_write(self, cmd, data, end=None):
        """Write data to target.

        Mimics simpleserial protocol of serial based targets.

        Args:
            cmd (str): Command to use. Target supports 'p' (write plaintext),
                and 'k' (write key).
            data (bytearray): Data to write to target
            end: Unused

        Raises:
            ValueError: Unknown command

        .. versionadded:: 5.1
            Added simpleserial_write to CW305
        """
        if cmd == 'p':
            self.loadInput(data)
            self.go()
        elif cmd == 'k':
            self.loadEncryptionKey(data)
        else:
            raise ValueError("Unknown command {}".format(cmd))

    def set_key(self, key, ack=False, timeout=250):
        """Checks if key is different from the last one sent. If so, send it.

        Args:
            key (bytearray):  key to send
            ack: Unused
            timeout: Unused

        .. versionadded:: 5.1
            Added set_key to CW305
        """
        if self.last_key != key:
            self.last_key = key
            self.simpleserial_write('k', key)

    def batchRun(self,
                 batchsize=1024,
                 random_key=True,
                 random_pt=True,
                 seed=None):
        """
            Run multiple encryptions on random data

            Args:
                batchsize (int): The number of encryption to run (default 1024).
                random_key (bool): True if the key is random (default False).
                random_pt (bool): True if the plaintext are random (default True).
                seed (int): random int32 for the PRG.
        """
        if seed is None:
            seed = random.randint(0, 2**32)

        data = []
        data.extend(
            packuint32(1 | (random_key << 1) | (random_pt << 2)
                       | (batchsize << 16)))
        data.extend(packuint32(seed))
        self.sam3u_write(0, data)

        # generate the inputs
        if random_key:
            key = [[0 for x in range(16)] for y in range(batchsize)]
        else:
            key = None

        if random_pt:
            pt = [[0 for x in range(16)] for y in range(batchsize)]
        else:
            pt = None

        for b in range(batchsize):
            if random_key:
                for j in range(16):
                    key[b][15 - j] = seed >> 24
                    seed += ((seed * seed) & 0xffffffff) | 0x5
                    seed &= 0xffffffff
            if random_pt:
                for j in range(16):
                    pt[b][15 - j] = seed >> 24
                    seed += ((seed * seed) & 0xffffffff) | 0x5
                    seed &= 0xffffffff
        return key, pt

    def sam3u_write(self, addr, data):
        """Write to an address on the FPGA

        Args:
            addr (int): Address to write to
            data (list): Data to write to addr

        Raises:
            IOError: User attempted to write to a read-only location
        """
        if addr < self._woffset_sam3U:
            raise IOError("Write to read-only location: 0x%04x" % addr)
        if len(data) > (256 + addr):
            raise IOError("Write will overflow at location: 0x%04x" % (256))

        return self._naeusb.cmdWriteSam3U(addr, data)

    @fw_ver_required(0, 30)
    def spi_mode(self, enable=True, timeout=200, bsfile=None):
        """Enter programming mode for the onboard SPI chip
        
        Reprograms the FPGA with the appropriate bitstream and 
        returns an object with which to program the CW305 SPI chip
        (see documentation on the returned object for more info)

        Args:
            enable (bool): Enable the SPI interface before returning it. Defaults to True
            timeout (int): USB timeout in ms. Defaults to 200.
            bsfile (string): If not None, program with a bitstream pointed to by bsfile.
                             If None, program with SPI passthrough bitstream for the chip
                             specified during connection (or cw.target()) 

        Returns:
            A FPGASPI object which can be used to erase/program/verify/read the SPI
            chip on the CW305.
        """
        from datetime import datetime
        if self._fpga_id is None and bsfile is None:
            logging.warning(
                "CW305 requires passthrough bitstream to program SPI chip, but file/chip not specified"
            )
        else:
            bsdata = None
            if self._fpga_id:
                from chipwhisperer.hardware.firmware.cw305 import getsome
                bsdata = getsome(f"SPI_flash_{self._fpga_id}.bit")
            else:
                bsdata = open(bsfile, "rb")
            starttime = datetime.now()
            status = self.fpga.FPGAProgram(bsdata, exceptOnDoneFailure=False)
            stoptime = datetime.now()
            if status:
                logging.info('FPGA Config OK, time: %s' %
                             str(stoptime - starttime))
            else:
                logging.warning(
                    'FPGA Done pin failed to go high, check bitstream is for target device.'
                )

        spi = FPGASPI(self._naeusb, timeout)
        spi.enable_interface(enable)
        return spi

    @fw_ver_required(0, 40)
    def gpio_mode(self, timeout=200):
        """Allow arbitrary GPIO access on SAM3U
        
        Allows low-level IO access to SAM3U GPIO, and also SPI transfers.
        (see documentation on the returned object for more info)

        Args:
            timeout (int): USB timeout in ms. Defaults to 200.

        Returns:
            A FPGAIO object which can be used to access IO on the CW305.
        """
        io = FPGAIO(self._naeusb, timeout)
        return io
Пример #6
0
class CWNano(ScopeTemplate, Plugin, util.DisableNewAttr):
    """CWNano scope object.

    This class contains the public API for the CWNano hardware. It includes
    specific settings for each of these devices.

    To connect to one of these devices, the easiest method is

    This code will automatically detect an attached ChipWhisperer device and
    connect to it.

    For more help about scope settings, try help() on each of the ChipWhisperer
    scope submodules:
        scope.adc
        scope.io
        scope.glitch
    """

    _name = "ChipWhisperer Nano"

    REQ_ARM = 0x29
    REQ_SAMPLES = 0x2A

    def __init__(self):
        ScopeTemplate.__init__(self)
        self._is_connected = False

        self.params.init()

        self._cwusb = NAEUSB()
        self.ser = self._cwusb
        self.scopetype = self
        self.dev = self

        self.xmega = XMEGAPDI(self._cwusb)
        self.avr = AVRISP(self._cwusb)
        self.usart = USART(self._cwusb)
        self.serialstm32f = STM32FSerial(cwserial=self.usart)
        self.serialstm32f.scope = self
        self.io = GPIOSettings(self._cwusb)
        self.adc = ADCSettings(self._cwusb)
        self.glitch = GlitchSettings(self._cwusb)
        self._timeout = 2

        self._lasttrace = None

        self.getParams().addChildren([
            {'name':"CW-Lite XMEGA Programmer", 'tip':"Open XMEGA Programmer (ChipWhisperer-Lite Only)", 'type':"menu", "action":lambda _:self.getCwliteXMEGA().show()},
            {'name':"CW-Lite AVR Programmer", 'tip':"Open AVR Programmer (ChipWhisperer-Lite Only)", 'type':"menu", "action":lambda _:self.getCwliteAVR().show()},
            {'name':'Serial STM32F Programmer', 'tip':"Open STM32F Programmer (Serial/ChipWhisperer)", 'type':"menu", "action":lambda _:self.getSerialSTM32F().show()}
        ])

        self.disable_newattr()

    def getCurrentScope(self):
        return self

    def _con(self):
        self._cwusb.con(idProduct=[0xACE0])
        self.disable_newattr()
        self._is_connected = True
        return True

    def _dis(self):
        self.enable_newattr()
        self._is_connected = False
        return True

    def arm(self):
        """Arm the ADC, the trigger will be GPIO4 rising edge (fixed trigger)."""
        if self.connectStatus.value() is False:
            raise Warning("Scope \"" + self.getName() + "\" is not connected. Connect it first...")

        self._cwusb.sendCtrl(self.REQ_ARM, 1)


    def capture(self):
        """Raises IOError if unknown failure, returns 'True' if timeout, 'False' if no timeout"""

        starttime = datetime.datetime.now()
        while self._cwusb.readCtrl(self.REQ_ARM, dlen=1)[0] == 0:
            # Wait for a moment before re-running the loop
            time.sleep(0.05)
            diff = datetime.datetime.now() - starttime

            # If we've timed out, don't wait any longer for a trigger
            if (diff.total_seconds() > self._timeout):
                logging.warning('Timeout in cwnano capture()')
                return True

        self._lasttrace = self._cwusb.cmdReadMem(0, self.adc.samples)

        self._lasttrace = np.array(self._lasttrace) / 256.0 - 0.5

        self.newDataReceived(0, self._lasttrace, 0, self.adc.clk_freq)

        return False


    def getLastTrace(self):
        """Return the last trace captured with this scope.
        """
        return self._lasttrace


    def _dict_repr(self):
        dict = OrderedDict()
        dict['io']    = self.io._dict_repr()
        dict['adc']   = self.adc._dict_repr()
        dict['glitch'] = self.glitch._dict_repr()
        return dict

    def __repr__(self):
        # Add some extra information about ChipWhisperer type here
        if self._is_connected:
            ret = "ChipWhisperer Nano Device\n"
            return ret + dict_to_str(self._dict_repr())
        else:
            ret = "ChipWhisperer Nano device (disconnected)"
            return ret

    def __str__(self):
        return self.__repr__()

    def get_possible_devices(self, idProduct):
        return self._cwusb.get_possible_devices(idProduct=idProduct)

    def usbdev(self):
        return self._cwusb

    def getCwliteXMEGA(self):
        if not hasattr(self, 'cwliteXMEGA'):
            self.enable_newattr()
            self.cwliteXMEGA = XMEGAProgrammerDialog()
            self.disable_newattr()
        return self.cwliteXMEGA

    def getCwliteAVR(self):
        if not hasattr(self, 'cwliteAVR'):
            self.enable_newattr()
            self.cwliteAVR = AVRProgrammerDialog()
            self.disable_newattr()
        return self.cwliteAVR

    def getSerialSTM32F(self):
        if not hasattr(self, 'serialSTM32F'):
            self.enable_newattr()
            self.serialSTM32F = STM32FProgrammerDialog()
            self.disable_newattr()
        return self.serialSTM32F
Пример #7
0
class CW310(CW305):
    def __init__(self, *args, **kwargs):
        # maybe later can hijack cw305 stuff, but for now don't
        pass
        import chipwhisperer as cw
        self._naeusb = NAEUSB()
        self.pll = PLLCDCE906(self._naeusb, ref_freq=12.0E6)
        self.fpga = FPGA(self._naeusb)

        self.hw = None
        self.oa = None

        self._woffset_sam3U = 0x000
        self.default_verilog_defines = 'cw305_defines.v'
        self.default_verilog_defines_full_path = os.path.dirname(
            cw.__file__
        ) + '/../../hardware/victims/cw305_artixtarget/fpga/common/' + self.default_verilog_defines
        self.registers = 12  # number of registers we expect to find
        self.bytecount_size = 7  # pBYTECNT_SIZE in Verilog

        self._clksleeptime = 1
        self._clkusbautooff = True
        self.last_key = bytearray([0] * 16)
        self.target_name = 'AES'

    def _con(self,
             scope=None,
             bsfile=None,
             force=False,
             fpga_id=None,
             defines_files=None,
             slurp=True):
        # add more stuff later
        self._naeusb.con(idProduct=[0xC310])
        # self.pll.cdce906init()

        if defines_files is None:
            if fpga_id is None:
                verilog_defines = [self.default_verilog_defines_full_path]
            else:
                from chipwhisperer.hardware.firmware.cw305 import getsome
                verilog_defines = [getsome(self.default_verilog_defines)]
        else:
            verilog_defines = defines_files
        if slurp:
            self.slurp_defines(verilog_defines)

        if bsfile:
            status = self.fpga.FPGAProgram(open(bsfile, "rb"))

    def usb_set_voltage(self, pdo_num, voltage):
        if pdo_num not in [2, 3]:
            raise ValueError("pdo_num must be 2 or 3, {}".format(pdo_num))
        if (voltage > 20 or voltage < 5):
            raise ValueError(
                "Voltage must be between 5 and 20, {}".format(voltage))
        self._naeusb.sendCtrl(0x40, 0, [0x28, 0x89 + (pdo_num - 2) * (0x04)])
        snk_pdo = self._naeusb.readCtrl(0x41, 0, 4)
        voltage *= 20
        voltage = int(voltage)

        snk_pdo[1] &= ~0xFC
        snk_pdo[2] &= ~0x0F
        print(snk_pdo)
        snk_pdo[1] |= ((voltage << 2) & 0xFC)
        snk_pdo[2] |= ((voltage >> 6) & 0x0F)
        print(voltage)
        print(snk_pdo)
        self._naeusb.sendCtrl(0x41, 0, snk_pdo)

    def usb_set_current(self, pdo_num, current):
        if pdo_num not in [2, 3]:
            raise ValueError("pdo_num must be 2 or 3, {}".format(pdo_num))
        if (current > 5 or current < 0.5):
            raise ValueError(
                "Voltage must be between 0.5 and 20, {}".format(voltage))
        snk_pdo = self._naeusb.readCtrl(0x41, 0, 4)
        current *= 100
        current = int(current)

        snk_pdo[0] &= ~0xFF
        snk_pdo[1] &= ~0x03
        print(snk_pdo)
        snk_pdo[0] |= (current & 0xFF)
        snk_pdo[1] |= ((current >> 8) & 0x03)
        print(current)
        print(snk_pdo)
        self._naeusb.sendCtrl(0x41, 0, snk_pdo)

    def usb_negotiate_pdo(self):
        #soft reset
        self._naeusb.sendCtrl(0x40, 0, [0x28, 0x51])
        self._naeusb.sendCtrl(0x41, 0, [0x0D])

        #send reset on pdo bus
        self._naeusb.sendCtrl(0x40, 0, [0x28, 0x1A])
        self._naeusb.sendCtrl(0x41, 0, [0x26])

    def _dis(self):
        if self._naeusb:
            self._naeusb.close()