def defineName(self): self.name = 'Partition Comparison' self.partObject = Partition(self) partModeList = {} for a in self.partObject.supportedMethods: partModeList[a.partitionType] = a self.diffObject = DifferenceMode() diffModeList = {} for a in self.diffObject.supportedMethods: diffModeList[a.differenceType] = a self.addGroup("generatePartitions") self.addGroup("generatePartitionStats") self.addGroup("generatePartitionDiffs") self.addGroup("displayPartitionDiffs") self.poi = POI(self) self.poidock = self.parent.addDock(self.poi, "Points of Interest", area=Qt.RightDockWidgetArea) self.poidock.hide() self.params = [ {'name':'Comparison Mode', 'key':'diffmode', 'type':'list', 'values':diffModeList, 'value':self.diffObject.diffMethodClass, 'set':self.updateScript}, {'name':'Partition Mode', 'key':'partmode', 'type':'list', 'values':partModeList, 'value':self.partObject.partMethodClass, 'set':self.updateScript}, {'name':'Display', 'type':'action', 'action':self.runAction}, {'name':'Auto-Save Data to Project', 'key':'part-saveints', 'type':'bool', 'value':False, 'set':self.updateScript}, {'name':'Auto-Load Data from Project', 'key':'part-loadints', 'type':'bool', 'value':False, 'set':self.updateScript}, {'name':'Points of Interest', 'key':'poi', 'type':'group', 'children':[ {'name':'Selection Mode', 'type':'list', 'values':{'Max N Points/Subkey':'maxn'}, 'value':'maxn'}, {'name':'Point Range', 'key':'poi-pointrng', 'type':'range', 'limits':(0, 0), 'set':self.updatePOI}, {'name':'Num POI/Subkey', 'key':'poi-nummax', 'type':'int', 'limits':(1, 200), 'value':1, 'set':self.updatePOI}, {'name':'Min Spacing between POI', 'key':'poi-minspace', 'type':'int', 'limits':(1, 100E6), 'value':1, 'step':100, 'set':self.updatePOI}, # {'name':'Threshold', 'key':'threshold', 'type':'int', 'visible':False}, {'name':'Open POI Table', 'type':'action', 'action':self.poidock.show}, ]}, ] self.updateScript()
def addTraces(self, tracedata, tracerange, progressBar=None, pointRange=None, attack=None): # NOTE: this is for calling substuff, not for legacy code here! if pointRange is None: pointRange = (0,-1) #--- roundwidth = 0 if attack is not None: round528 = attack.keeloq_round528 roundwidth = attack.keeloq_roundwidth # print "Using KEELOQ timing values from attack: %d %d" % (round528, roundwidth) if self.config['roundtiming'] != True: roundwidth = 0 #--- prepare environment partClass = keeloqPartition_CiphertextMSB partObject = Partition() partObject.setPartMethod(partClass) partObject.setTraceSource(tracedata) partDisplay = PartitionDisplay(None) partDisplay._traces = tracedata if progressBar: progressBar.setMaximum(64 + 1) #--- loop for all unknown keybits keystream = "" for bit in range(0, 64): #--- Analize all possible values (0 and 1) guessNum = 2 guessDiffs = [] * guessNum #--- Partition and find differences using T-Test if bit==48: print "\nTODO: Sorry for becoming slower, the cipher implementation has not been optimized yet.\n" for guessBit in range (0, guessNum): # Probe known keystream + guessbit + dummybit # # The dummybit is necessary because of a property of Keeloq. The last bit of the stream is # mixed into the attacked data bit with XOR. This results in exactly complementary partitioning # for values 0 vs 1, making it impossible to pick a "winner". Therefore we append the dummybit # at this position, so that guessbit is the last input that actually makes a measureable difference. partData = partObject.generatePartitions(partitionClass=None, saveFile=False, loadFile=False, tRange=tracerange, partitionConfig={"keystream":"%s %d 0" % (keystream, guessBit)}) partStats = partDisplay.generatePartitionStats(partitionData={"partclass":partClass, "partdata":partData}, saveFile=False, loadFile=False, tRange=tracerange, pointRange=pointRange) partDiffs = partDisplay.generatePartitionDiffs(DifferenceModeTTest, statsInfo={"partclass":partClass, "stats":partStats}, saveFile=False, loadFile=False, tRange=tracerange) guessDiffs.append(partDiffs[0]) # TODO: since we calculate 0 and 1 anyway we should call the partition mode just once and use a mode that produces 2 keys (0,1) #--- Determine range in which high correlation is expected # TODO: for strict round timing, it is faster to do partStats only for the desired pointRange if roundwidth <= 0: # look in whole pointRange lookStart = 0 lookStop = len(guessDiffs[0]) else: # look only at expected position (enforce round timing) round = 528 - 32 - (bit+1) roundStart = round528 - ((528-round) * roundwidth) roundStop = roundStart + roundwidth lookStart = roundStart - pointRange[0] lookStop = roundStop - pointRange[0] # print "Round=%d Pos=%d-%d look=%d-%d" % (round, roundStart, roundStop, lookStart, lookStop) if (lookStart < 0) or (lookStop > len(guessDiffs[0])): print "Enforced round timing range (%d,%d) is outside of analyzed pointRange(%d,%d). "\ "Can't detect key bit %d. Result so far: %s" %\ (roundStart, roundStop, pointRange[0], pointRange[1], bit, keystream) return #--- Detect highest correlation # print "Looking at range %d-%d of %d" % (lookStart, lookStop, len(guessDiffs[0])) # if bit == 0: # print guessDiffs[0][lookStart:lookStop] # print guessDiffs[1][lookStart:lookStop] winBit = 0 if (np.nanmax(guessDiffs[0][lookStart:lookStop]) > np.nanmax(guessDiffs[1][lookStart:lookStop])) else 1 badBit = winBit ^ 1 winOffset = np.argmax(guessDiffs[winBit][lookStart:lookStop]) + lookStart badOffset = np.argmax(guessDiffs[badBit][lookStart:lookStop]) + lookStart winDiff = guessDiffs[winBit][winOffset] badDiff = guessDiffs[badBit][badOffset] winRatioSpot = winDiff / guessDiffs[badBit][winOffset] winRatioTrace = winDiff / badDiff keystream = "%s%d" % (keystream, winBit) #--- report print "Analysis of keybit #%02d (of 64): %d (diff=%f spot=%f trace=%f pos=%d) vs %d (diff=%f pos=%d)" %\ (bit, winBit, winDiff, winRatioSpot, winRatioTrace, winOffset + pointRange[0], badBit, badDiff, badOffset + pointRange[0]) if progressBar: progressBar.setText("Attacking key bits (%d of 64)" % bit) progressBar.setStatusMask("key=%s\nkeystream=%s" % (keeloqFormatKeystream(keystream), keystream)) progressBar.updateStatus(bit) if progressBar.wasAborted(): print "Aborting. Result so far: %s" % keystream return #--- keyStr = "%s%s" % (keystream[16:64], keystream[0:16]) keyInt = int(keyStr, 2) print "Keystream = %s" % keystream print "Key: %016x" % keyInt if progressBar: progressBar.setText("Attack finished") progressBar.setStatusMask("Key: %016x" % keyInt) progressBar.updateStatus(64) #--- Decrypt the data associated with all traces progress = 64 if True: start = tracerange[0] end = tracerange[1] if end == -1: end = tracedata.numTraces() tnum = start while tnum < end: t = tracedata.getSegment(tnum) # Discover where this trace starts & ends tmapstart = t.mappedRange[0] tmapend = t.mappedRange[1] if progressBar: increment = tmapend + 1 - tmapstart progressBar.setMaximum(progress + increment + 1) progressBar.setText("Decrypting traces") for tnum in range(tmapstart, tmapend + 1): if progressBar: progressBar.updateStatus(progress) progress += 1 if progressBar.wasAborted(): return textout = t.getTextout(tnum - tmapstart) if (textout is not None) and (len(textout) >= 4): data = (textout[0] << 24) | (textout[1] << 16) | (textout[2] << 8) | (textout[3] << 0) decrypt = keeloqDecrypt(data, keyInt) print "Trace %0d: %08x -> %08x" % (tnum - tmapstart, data, decrypt) tnum = tmapend + 1 print "Key: %016x" % keyInt #--- cleanup if progressBar: progressBar.setMaximum(100) progressBar.updateStatus(100)
def defineName(self): self.partObject = Partition() partModeList = {} for a in self.partObject.supportedMethods: partModeList[a.partitionType] = a self.diffObject = DifferenceMode() diffModeList = {} for a in self.diffObject.supportedMethods: diffModeList[a.differenceType] = a self.addGroup("generatePartitions") self.addGroup("generatePartitionStats") self.addGroup("generatePartitionDiffs") self.addGroup("displayPartitionDiffs") self.getParams().addChildren([ { 'name': 'Comparison Mode', 'key': 'diffmode', 'type': 'list', 'values': diffModeList, 'value': self.diffObject.diffMethodClass, 'action': lambda _: self.updateScript() }, { 'name': 'Partition Mode', 'key': 'partmode', 'type': 'list', 'values': partModeList, 'value': self.partObject.partMethodClass, 'action': lambda _: self.updateScript() }, { 'name': 'Display', 'type': 'action', 'action': lambda _: self.runAction() }, { 'name': 'Auto-Save Data to Project', 'key': 'part-saveints', 'type': 'bool', 'value': False, 'action': lambda _: self.updateScript() }, { 'name': 'Auto-Load Data from Project', 'key': 'part-loadints', 'type': 'bool', 'value': False, 'action': lambda _: self.updateScript() }, { 'name': 'Points of Interest', 'key': 'poi', 'type': 'group', 'children': [ { 'name': 'Selection Mode', 'type': 'list', 'values': { 'Max N Points/Subkey': 'maxn' }, 'value': 'maxn' }, { 'name': 'Point Range', 'key': 'poi-pointrng', 'type': 'range', 'limits': (0, 0), 'default': (0, 0), 'value': (0, 0), 'action': lambda _: self.updatePOI() }, { 'name': 'Num POI/Subkey', 'key': 'poi-nummax', 'type': 'int', 'limits': (1, 200), 'value': 1, 'action': lambda _: self.updatePOI() }, { 'name': 'Min Spacing between POI', 'key': 'poi-minspace', 'type': 'int', 'limits': (1, 100E6), 'value': 1, 'step': 100, 'action': lambda _: self.updatePOI() }, { 'name': 'Hill detection', 'key': 'poi-hilldet', 'type': 'bool', 'value': True, 'tip': "Extend the bounds downhill for each peak", 'action': lambda _: self.updatePOI() }, # {'name':'Threshold', 'key':'threshold', 'type':'int', 'visible':False}, { 'name': 'Open POI Table', 'type': 'action', 'action': lambda _: self.poiDock.show() }, ] }, ])
def __init__(self, tmanager=None): self._traceSource = None self.partObject = Partition()
def __init__(self, tmanager=None): self._tmanager = None self.partObject = Partition(self)