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)
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
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)
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
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
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
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()