def test_find_generated_crc16(self): mb = MessageTypeBuilder("data") mb.add_label(FieldType.Function.PREAMBLE, 8) mb.add_label(FieldType.Function.SYNC, 16) mb.add_label(FieldType.Function.LENGTH, 8) mb.add_label(FieldType.Function.DATA, 32) mb.add_checksum_label(16, GenericCRC.from_standard_checksum("CRC16 CCITT")) mb2 = MessageTypeBuilder("data2") mb2.add_label(FieldType.Function.PREAMBLE, 8) mb2.add_label(FieldType.Function.SYNC, 16) mb2.add_label(FieldType.Function.LENGTH, 8) mb2.add_label(FieldType.Function.DATA, 16) mb2.add_checksum_label(16, GenericCRC.from_standard_checksum("CRC16 CCITT")) pg = ProtocolGenerator([mb.message_type, mb2.message_type], syncs_by_mt={mb.message_type: "0x1234", mb2.message_type: "0x1234"}) num_messages = 5 for i in range(num_messages): pg.generate_message(data="{0:032b}".format(i), message_type=mb.message_type) pg.generate_message(data="{0:016b}".format(i), message_type=mb2.message_type) #self.save_protocol("crc16_test", pg) self.clear_message_types(pg.protocol.messages) ff = FormatFinder(pg.protocol.messages) ff.run() self.assertEqual(len(ff.message_types), 2) for mt in ff.message_types: checksum_label = mt.get_first_label_with_type(FieldType.Function.CHECKSUM) self.assertEqual(checksum_label.length, 16) self.assertEqual(checksum_label.checksum.caption, "CRC16 CCITT")
def test_bruteforce_parameters_and_data_range_improved(self): c = GenericCRC(polynomial="16_ccitt", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) inpt = "101010101010101010000000111000000000000011100000001011010010110100000000111000000101001010000100000000000100111001111110010000000011011111111001001101100001100010100000000000111011110100010" vrfy_crc = "0011101111010001" t1 = 0 runs = 100 for i in range(0, runs): t = time.time() result = c.bruteforce_parameters_and_data_range( c.str2arr(inpt), len(inpt) - len(vrfy_crc) - 1) t1 += time.time() - t # print(result, c.bit2str(c.crc(c.str2arr(inpt[result[1]:result[2]])))) self.assertEqual(result[0], 2) # Parameters = 2 self.assertEqual(result[1], len(inpt) - 1 - 16 - 88) # start of datarange self.assertEqual(result[2], len(inpt) - 1 - 16) # end of datarange inpt = "0" + inpt if i % 2 == 0 else "1" + inpt # print("Performance:", t1/runs) self.assertLess(t1 / runs, 0.1) # Should be faster than 100ms in average
def test_configure_crc_parameters(self): crc_label = ChecksumLabel("crc_label", 25, 120, 0, FieldType("crc", FieldType.Function.CHECKSUM)) crc_widget_controller = ChecksumWidgetController(crc_label, Message([0] * 150, 0, MessageType("test")), 0) crc = GenericCRC(polynomial=list(GenericCRC.DEFAULT_POLYNOMIALS.keys())[0]) self.assertEqual(crc_widget_controller.ui.lineEditCRCPolynomial.text(), crc.polynomial_as_hex_str) self.assertEqual(crc_widget_controller.ui.lineEditStartValue.text(), util.bit2hex(crc.start_value)) self.assertEqual(crc_widget_controller.ui.lineEditFinalXOR.text(), util.bit2hex(crc.final_xor)) crc_widget_controller.ui.comboBoxCRCFunction.setCurrentIndex(2) crc.polynomial = crc.choose_polynomial(2) self.assertEqual(crc_widget_controller.ui.lineEditCRCPolynomial.text(), crc.polynomial_as_hex_str) crc_widget_controller.ui.lineEditCRCPolynomial.setText("abcde") crc_widget_controller.ui.lineEditCRCPolynomial.editingFinished.emit() self.assertEqual(crc_label.checksum.polynomial, array.array("B", [1]) + util.hex2bit("abcde")) crc_widget_controller.ui.lineEditStartValue.setText("12345") crc_widget_controller.ui.lineEditStartValue.editingFinished.emit() self.assertEqual(util.bit2hex(crc_label.checksum.start_value), "12345") crc_widget_controller.ui.lineEditFinalXOR.setText("cccaa") crc_widget_controller.ui.lineEditFinalXOR.editingFinished.emit() self.assertEqual(util.bit2hex(crc_label.checksum.final_xor), "cccaa")
def test_configure_crc_parameters(self): crc_label = ChecksumLabel( "crc_label", 25, 120, 0, FieldType("crc", FieldType.Function.CHECKSUM)) crc_widget_controller = ChecksumWidgetController( crc_label, Message([0] * 150, 0, MessageType("test")), 0) crc = GenericCRC( polynomial=list(GenericCRC.DEFAULT_POLYNOMIALS.keys())[0]) self.assertEqual(crc_widget_controller.ui.lineEditCRCPolynomial.text(), crc.polynomial_as_hex_str) self.assertEqual(crc_widget_controller.ui.lineEditStartValue.text(), util.bit2hex(crc.start_value)) self.assertEqual(crc_widget_controller.ui.lineEditFinalXOR.text(), util.bit2hex(crc.final_xor)) crc_widget_controller.ui.comboBoxCRCFunction.setCurrentIndex(2) crc.polynomial = crc.choose_polynomial(2) self.assertEqual(crc_widget_controller.ui.lineEditCRCPolynomial.text(), crc.polynomial_as_hex_str) crc_widget_controller.ui.lineEditCRCPolynomial.setText("abcde") crc_widget_controller.ui.lineEditCRCPolynomial.editingFinished.emit() self.assertEqual(crc_label.checksum.polynomial, array.array("B", [1]) + util.hex2bit("abcde")) crc_widget_controller.ui.lineEditStartValue.setText("12345") crc_widget_controller.ui.lineEditStartValue.editingFinished.emit() self.assertEqual(util.bit2hex(crc_label.checksum.start_value), "12345") crc_widget_controller.ui.lineEditFinalXOR.setText("cccaa") crc_widget_controller.ui.lineEditFinalXOR.editingFinished.emit() self.assertEqual(util.bit2hex(crc_label.checksum.final_xor), "cccaa")
def test_crc(self): # http://depa.usst.edu.cn/chenjq/www2/software/crc/CRC_Javascript/CRCcalculation.htm # CRC-16: polynomial="16_standard", start_value = False, final_xor = False, reverse_polynomial=False, reverse_all=False # CRC-16-CCITT: polynomial="16_ccitt", start_value = False, final_xor = False, reverse_polynomial=False, reverse_all=False # http://www.lammertbies.nl/comm/info/crc-calculation.html <- Fehler # CRC-16: polynomial="16_standard", start_value = False, final_xor = False, reverse_polynomial=False, reverse_all=False c = GenericCRC(polynomial=WSPChecksum.CRC_8_POLYNOMIAL) e = Encoding() bitstr = [ "010101010110100111011010111011101110111011100110001011101010001011101110110110101101", "010101010110101001101110111011101110111011100110001011101010001011101110110111100101", "010101010110100111010010111011101110111011100110001011101010001011101110110110100101" ] expected = ["78", "c9", "f2"] for value, expect in zip(bitstr, expected): nv = "" for i in range(0, len(value)): if value[i] == "1": nv += "0" else: nv += "1" self.assertEqual(util.bit2hex(c.crc(e.str2bit(value[4:-8]))), expect)
def test_crc(self): # http://depa.usst.edu.cn/chenjq/www2/software/crc/CRC_Javascript/CRCcalculation.htm # CRC-16: polynomial="16_standard", start_value = False, final_xor = False, reverse_polynomial=False, reverse_all=False # CRC-16-CCITT: polynomial="16_ccitt", start_value = False, final_xor = False, reverse_polynomial=False, reverse_all=False # http://www.lammertbies.nl/comm/info/crc-calculation.html <- Fehler # CRC-16: polynomial="16_standard", start_value = False, final_xor = False, reverse_polynomial=False, reverse_all=False c = GenericCRC(polynomial=WSPChecksum.CRC_8_POLYNOMIAL) e = Encoding() bitstr = ["010101010110100111011010111011101110111011100110001011101010001011101110110110101101", "010101010110101001101110111011101110111011100110001011101010001011101110110111100101", "010101010110100111010010111011101110111011100110001011101010001011101110110110100101"] expected = ["78", "c9", "f2"] for value, expect in zip(bitstr, expected): nv = "" for i in range(0, len(value)): if value[i] == "1": nv += "0" else: nv += "1" self.assertEqual(util.bit2hex(c.crc(e.str2bit(value[4:-8]))), expect)
def test_crc8(self): messages = ["aabbcc", "abcdee", "dacafe"] expected = ["7d", "24", "33"] crc = GenericCRC(polynomial=GenericCRC.DEFAULT_POLYNOMIALS["8_ccitt"]) for msg, expect in zip(messages, expected): bits = util.hex2bit(msg) self.assertEqual(util.bit2hex(crc.crc(bits)), expect)
def test_cache(self): c = GenericCRC(polynomial="16_standard", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) c.calculate_cache(8) self.assertEqual(len(c.cache), 256)
def test_find_crc8(self): messages = ["aabbcc7d", "abcdee24", "dacafe33"] message_bits = [np.array(msg, dtype=np.uint8) for msg in map(util.hex2bit, messages)] checksum_engine = ChecksumEngine(message_bits, n_gram_length=8) result = checksum_engine.find() self.assertEqual(len(result), 1) checksum_range = result[0] # type: ChecksumRange self.assertEqual(checksum_range.length, 8) self.assertEqual(checksum_range.start, 24) reference = GenericCRC() reference.set_polynomial_from_hex("0x07") self.assertEqual(checksum_range.crc.polynomial, reference.polynomial) self.assertEqual(checksum_range.message_indices, {0, 1, 2})
def _prepare_protocol_3() -> ProtocolGenerator: alice = Participant("Alice", address_hex="1337") bob = Participant("Bob", address_hex="beef") checksum = GenericCRC.from_standard_checksum("CRC8 CCITT") mb = MessageTypeBuilder("data") mb.add_label(FieldType.Function.PREAMBLE, 16) mb.add_label(FieldType.Function.SYNC, 16) mb.add_label(FieldType.Function.LENGTH, 8) mb.add_label(FieldType.Function.SRC_ADDRESS, 16) mb.add_label(FieldType.Function.DST_ADDRESS, 16) mb.add_label(FieldType.Function.SEQUENCE_NUMBER, 8) mb.add_label(FieldType.Function.DATA, 10 * 8) mb.add_checksum_label(8, checksum) mb_ack = MessageTypeBuilder("ack") mb_ack.add_label(FieldType.Function.PREAMBLE, 16) mb_ack.add_label(FieldType.Function.SYNC, 16) mb_ack.add_label(FieldType.Function.LENGTH, 8) mb_ack.add_label(FieldType.Function.DST_ADDRESS, 16) mb_ack.add_checksum_label(8, checksum) pg = ProtocolGenerator([mb.message_type, mb_ack.message_type], syncs_by_mt={ mb.message_type: "0x9a7d", mb_ack.message_type: "0x9a7d" }, preambles_by_mt={ mb.message_type: "10" * 8, mb_ack.message_type: "10" * 8 }, participants=[alice, bob]) return pg
def __set_crc_function_index(self): # Get the combobox index crc_found = False for crc_name in GenericCRC.DEFAULT_POLYNOMIALS: test_crc = GenericCRC(crc_name) if test_crc == self.checksum_label.checksum: self.ui.comboBoxCRCFunction.setCurrentText(crc_name) crc_found = True break if not crc_found: for crc_name, crc in self.SPECIAL_CRCS.items(): if self.checksum_label.checksum == crc: self.ui.comboBoxCRCFunction.setCurrentText(crc_name) crc_found = True break if not crc_found: self.__add_and_select_custom_item() elif "Custom" in [ self.ui.comboBoxCRCFunction.itemText(i) for i in range(self.ui.comboBoxCRCFunction.count()) ]: self.ui.comboBoxCRCFunction.removeItem( self.ui.comboBoxCRCFunction.count() - 1)
def test_find_crc16(self): messages = ["12345678347B", "abcdefffABBD", "cafe1337CE12"] message_bits = [np.array(msg, dtype=np.uint8) for msg in map(util.hex2bit, messages)] checksum_engine = ChecksumEngine(message_bits, n_gram_length=8) result = checksum_engine.find() self.assertEqual(len(result), 1) checksum_range = result[0] # type: ChecksumRange self.assertEqual(checksum_range.start, 32) self.assertEqual(checksum_range.length, 16) reference = GenericCRC() reference.set_polynomial_from_hex("0x8005") self.assertEqual(checksum_range.crc.polynomial, reference.polynomial) self.assertEqual(checksum_range.message_indices, {0, 1, 2})
def test_find_crc32(self): messages = ["deadcafe5D7F3F5A", "47111337E3319242", "beefaffe0DCD0E15"] message_bits = [np.array(msg, dtype=np.uint8) for msg in map(util.hex2bit, messages)] checksum_engine = ChecksumEngine(message_bits, n_gram_length=8) result = checksum_engine.find() self.assertEqual(len(result), 1) checksum_range = result[0] # type: ChecksumRange self.assertEqual(checksum_range.start, 32) self.assertEqual(checksum_range.length, 32) reference = GenericCRC() reference.set_polynomial_from_hex("0x04C11DB7") self.assertEqual(checksum_range.crc.polynomial, reference.polynomial) self.assertEqual(checksum_range.message_indices, {0, 1, 2})
def test_guess_standard_parameters_and_datarange(self): c = GenericCRC(polynomial="16_ccitt", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) inpt = "101010101010101010000000111000000000000011100000001011010010110100000000111000000101001010000100000000000100111001111110010000000011011111111001001101100001100010100000000000111011110100010" vrfy_crc = "0011101111010001" result = c.guess_standard_parameters_and_datarange(c.str2arr(inpt), c.str2arr(vrfy_crc)) self.assertEqual(result, (2, 84, 172)) self.assertEqual(vrfy_crc, c.bit2str(c.crc(c.str2arr(inpt[result[1]:result[2]]))))
def __init__(self, name: str, start: int, end: int, color_index: int, field_type: FieldType, fuzz_created=False, auto_created=False, data_range_start=0): assert field_type.function == FieldType.Function.CHECKSUM super().__init__(name, start, end, color_index, fuzz_created, auto_created, field_type) self.__category = self.Category.generic self.__data_ranges = [[data_range_start, self.start]] # type: list[list[int,int]] self.checksum = GenericCRC(polynomial=0) # type: GenericCRC or WSPChecksum
def category(self, value: Category): if value != self.category: self.__category = value if self.category == self.Category.generic: self.checksum = GenericCRC() elif self.category == self.Category.wsp: self.checksum = WSPChecksum() else: raise ValueError("Unknown Category")
def test_reverse_engineering(self): c = GenericCRC(polynomial="16_standard", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) bitstring_set = [ "1110001111001011100010000101010100000010110111000101100010100100111110111101100110110111011001010010001011101010", "1110010011001011100010000101010100000010110111000101100010100100111110111101100110110111011001010010001011101010", "1110010111001011100010000101010100000010110111000101100010100100111110111101100110110111011001010010001011101010", "1110011011001011100010000101010100000010110111000101100010100100111110111101100110110111011001010010001011101010"] bitset = [] crcset = [] for i in bitstring_set: tmp = c.str2bit(i) bitset.append(tmp) crcset.append(c.crc(tmp)) # print(c.guess_standard_parameters(bitset[0], crcset[0])) polynomial = c.reverse_engineer_polynomial(bitset, crcset) if polynomial: self.assertEqual(c.bit2str(polynomial), "1000000000000101") self.assertEqual(util.bit2hex(polynomial), "8005")
def test_reverse_engineering(self): c = GenericCRC(polynomial="16_standard", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) bitstring_set = [ "1110001111001011100010000101010100000010110111000101100010100100111110111101100110110111011001010010001011101010", "1110010011001011100010000101010100000010110111000101100010100100111110111101100110110111011001010010001011101010", "1110010111001011100010000101010100000010110111000101100010100100111110111101100110110111011001010010001011101010", "1110011011001011100010000101010100000010110111000101100010100100111110111101100110110111011001010010001011101010" ] bitset = [] crcset = [] for i in bitstring_set: tmp = c.str2bit(i) bitset.append(tmp) crcset.append(c.crc(tmp)) polynomial = c.reverse_engineer_polynomial(bitset, crcset) if polynomial: self.assertEqual(c.bit2str(polynomial), "1000000000000101") self.assertEqual(util.bit2hex(polynomial), "8005")
def test_not_aligned_data_len(self): c = GenericCRC(polynomial="16_standard", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) polynomials = ["8_standard", "16_standard", "16_ccitt", "16_dnp"] crcs = {"8_standard": 0xd5, "16_standard": 0x8005, "16_ccitt": 0x1021, "16_dnp": 0x3d65} for j in polynomials: c.polynomial = c.choose_polynomial(j) inpt = "1" for i in range(0, 32): val = c.bit2int(c.crc(c.str2bit(inpt))) self.assertEqual(val, crcs[j]) inpt = "0" + inpt
def _prepare_protocol_4() -> ProtocolGenerator: alice = Participant("Alice", address_hex="1337") bob = Participant("Bob", address_hex="beef") checksum = GenericCRC.from_standard_checksum("CRC16 CCITT") mb = MessageTypeBuilder("data1") mb.add_label(FieldType.Function.PREAMBLE, 16) mb.add_label(FieldType.Function.SYNC, 16) mb.add_label(FieldType.Function.LENGTH, 8) mb.add_label(FieldType.Function.SRC_ADDRESS, 16) mb.add_label(FieldType.Function.DST_ADDRESS, 16) mb.add_label(FieldType.Function.DATA, 8 * 8) mb.add_checksum_label(16, checksum) mb2 = MessageTypeBuilder("data2") mb2.add_label(FieldType.Function.PREAMBLE, 16) mb2.add_label(FieldType.Function.SYNC, 16) mb2.add_label(FieldType.Function.LENGTH, 8) mb2.add_label(FieldType.Function.SRC_ADDRESS, 16) mb2.add_label(FieldType.Function.DST_ADDRESS, 16) mb2.add_label(FieldType.Function.DATA, 64 * 8) mb2.add_checksum_label(16, checksum) mb_ack = MessageTypeBuilder("ack") mb_ack.add_label(FieldType.Function.PREAMBLE, 16) mb_ack.add_label(FieldType.Function.SYNC, 16) mb_ack.add_label(FieldType.Function.LENGTH, 8) mb_ack.add_label(FieldType.Function.DST_ADDRESS, 16) mb_ack.add_checksum_label(16, checksum) mt1, mt2, mt3 = mb.message_type, mb2.message_type, mb_ack.message_type preamble = "10001000" * 2 pg = ProtocolGenerator([mt1, mt2, mt3], syncs_by_mt={ mt1: "0x9a7d", mt2: "0x9a7d", mt3: "0x9a7d" }, preambles_by_mt={ mt1: preamble, mt2: preamble, mt3: preamble }, participants=[alice, bob]) return pg
def from_xml(cls, tag: ET.Element, field_types_by_type_id=None): lbl = super().from_xml(tag, field_types_by_type_id) result = cls.from_label(lbl) result.data_ranges = ast.literal_eval(tag.get("data_ranges", "[]")) result.category = cls.Category[tag.get("category", "generic")] crc_tag = tag.find("crc") if crc_tag is not None: result.checksum = GenericCRC.from_xml(crc_tag) wsp_tag = tag.find("wsp_checksum") if wsp_tag is not None: result.checksum = WSPChecksum.from_xml(wsp_tag) return result
def _prepare_protocol_7() -> ProtocolGenerator: alice = Participant("Alice", address_hex="313370") bob = Participant("Bob", address_hex="031337") charly = Participant("Charly", address_hex="110000") daniel = Participant("Daniel", address_hex="001100") # broadcast = Participant("Broadcast", address_hex="ff") #TODO: Sometimes messages to broadcast checksum = GenericCRC.from_standard_checksum("CRC16 CC1101") mb = MessageTypeBuilder("data") mb.add_label(FieldType.Function.PREAMBLE, 16) mb.add_label(FieldType.Function.SYNC, 16) mb.add_label(FieldType.Function.LENGTH, 8) mb.add_label(FieldType.Function.DST_ADDRESS, 24) mb.add_label(FieldType.Function.SRC_ADDRESS, 24) mb.add_label(FieldType.Function.DATA, 8 * 8) mb.add_checksum_label(16, checksum) mb_ack = MessageTypeBuilder("ack") mb_ack.add_label(FieldType.Function.PREAMBLE, 8) mb_ack.add_label(FieldType.Function.SYNC, 16) mb_ack.add_label(FieldType.Function.DST_ADDRESS, 24) mb_ack.add_checksum_label(16, checksum) mb_kex = MessageTypeBuilder("kex") mb_kex.add_label(FieldType.Function.PREAMBLE, 24) mb_kex.add_label(FieldType.Function.SYNC, 16) mb_kex.add_label(FieldType.Function.DST_ADDRESS, 24) mb_kex.add_label(FieldType.Function.SRC_ADDRESS, 24) mb_kex.add_label(FieldType.Function.DATA, 64 * 8) mb_kex.add_checksum_label(16, checksum) pg = ProtocolGenerator( [mb.message_type, mb_ack.message_type, mb_kex.message_type], syncs_by_mt={ mb.message_type: "0x0420", mb_ack.message_type: "0x2222", mb_kex.message_type: "0x6767" }, preambles_by_mt={ mb.message_type: "10" * 8, mb_ack.message_type: "10" * 4, mb_kex.message_type: "10" * 12 }, participants=[alice, bob, charly, daniel]) return pg
def test_adaptive_crc_calculation(self): c = GenericCRC(polynomial="16_ccitt", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) inpt1 = "10101010101010" inpt2 = "1010101010101001" crc1 = c.crc(c.str2arr(inpt1)) crc2 = c.crc(c.str2arr(inpt2)) # Compute crc2 from crc1 in a faster way # Note: In general only forward direction delta = "01" c.start_value = crc1 crcx = c.crc(c.str2arr(delta)) self.assertEqual(crcx, crc2)
def from_xml(cls, tag: ET.Element, field_types_by_caption=None): lbl = super().from_xml(tag, field_types_by_caption) if lbl.field_type is None or lbl.field_type.function != FieldType.Function.CHECKSUM: checksum_field_type = next( (ft for ft in field_types_by_caption.values() if ft.function == FieldType.Function.CHECKSUM), FieldType("checksum", FieldType.Function.CHECKSUM, display_format_index=1)) lbl.field_type = checksum_field_type result = cls.from_label(lbl) result.data_ranges = ast.literal_eval(tag.get("data_ranges", "[]")) result.category = cls.Category[tag.get("category", "generic")] crc_tag = tag.find("crc") if crc_tag is not None: result.checksum = GenericCRC.from_xml(crc_tag) wsp_tag = tag.find("wsp_checksum") if wsp_tag is not None: result.checksum = WSPChecksum.from_xml(wsp_tag) return result
def apply_data_whitening(self, decoding, inpt): len_sync = len(self.data_whitening_sync) len_polynomial = len(self.data_whitening_polynomial) inpt_from = 0 inpt_to = len(inpt) # Crop last bit, if duplicate if decoding and inpt_to > 1: if inpt[-1] == inpt[-2]: inpt_to -= 1 # # Crop last bit, if len not multiple of 8 # if decoding and inpt_to % 8 != 0: # inpt_to -= (8 - (inpt_to % 8)) % 8 # inpt empty, polynomial or syncbytes are zero! (Shouldn't happen) if inpt_to < 1 or len_polynomial < 1 or len_sync < 1: return inpt[ inpt_from:inpt_to], 0, self.ErrorState.MISC # Misc Error # Search for whitening start position (after sync bytes) whitening_start_pos = inpt_from i = inpt_from while i < (inpt_to - len_sync): equalbits = 0 for j in range(0, len_sync): if inpt[i + j] == self.data_whitening_sync[j]: equalbits += 1 else: continue if len_sync == equalbits: whitening_start_pos = i + j + 1 break else: i += 1 # Sync not found if decoding and whitening_start_pos == inpt_from: return inpt[inpt_from:inpt_to], 0, self.ErrorState.SYNC_NOT_FOUND # Prepare keystream self.lfsr_state = array.array("B", []) keystream = self.lfsr(0) for i in range(whitening_start_pos, inpt_to, 8): keystream.extend(self.lfsr(8)) # If data whitening polynomial is wrong, keystream can be less than needed. Check and exit. if len(keystream) < inpt_to - whitening_start_pos: return inpt[ inpt_from:inpt_to], 0, self.ErrorState.MISC # Error 31338 # Overwrite crc16 in encoding case if not decoding and self.cc1101_overwrite_crc: # Remove additional bits offset = inpt_to % 8 data_end = inpt_to - 16 - offset c = GenericCRC(polynomial="16_standard", start_value=True) crc = c.crc(inpt[whitening_start_pos:data_end]) for i in range(0, 16): inpt[data_end + i] = crc[i] # Apply keystream (xor) for i in range(whitening_start_pos, inpt_to): inpt[i] ^= keystream[i - whitening_start_pos] # Duplicate last bit when encoding if not decoding: inpt += array.array("B", [inpt[-1]]) inpt_to += 1 return inpt[inpt_from:inpt_to], 0, self.ErrorState.SUCCESS
def test_different_crcs_fast(self): c = GenericCRC(polynomial="16_standard", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) bitstring_set = [ "10101010", "00000001", "000000010", "000000011", "0000000100000001", "101001001010101010101011101111111000000000000111101010011101011", "101001001010101101111010110111101010010110111010", "00000000000000000000000000000000100000000000000000000000000000000001111111111111", "1111111111111111111111111111111110111111111111111111110111111111111111110000000000" "1" ] for j in c.DEFAULT_POLYNOMIALS: c.polynomial = c.choose_polynomial(j) for i in bitstring_set: for cache in [8, 4, 7, 12, 16]: c.calculate_cache(cache) # Standard crc_new = c.cached_crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_old, crc_new) # Special final xor c.final_xor = c.str2bit("0000111100001111") crc_new = c.cached_crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_old, crc_new) c.final_xor = [False] * 16 # Special start value c.start_value = c.str2bit("1010101010101010") crc_new = c.cached_crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_old, crc_new) c.start_value = [False] * 16 # little_endian c.little_endian = True crc_new = c.cached_crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_old, crc_new) c.little_endian = False # reverse all c.reverse_all = True crc_new = c.cached_crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_old, crc_new) c.reverse_all = False # reverse_polynomial # We need to clear the cache before and after c.cache = [] # c.reverse_polynomial = True crc_new = c.cached_crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_old, crc_new) c.reverse_polynomial = False # c.cache = [] # TODO: Does only work for cachesize = 8 # lsb_first c.calculate_cache(8) c.lsb_first = True crc_new = c.cached_crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_old, crc_new) c.lsb_first = False
def generate_homematic(cls, num_messages: int, save_protocol=True): mb_m_frame = MessageTypeBuilder("mframe") mb_c_frame = MessageTypeBuilder("cframe") mb_r_frame = MessageTypeBuilder("rframe") mb_a_frame = MessageTypeBuilder("aframe") participants = [ Participant("CCU", address_hex="3927cc"), Participant("Switch", address_hex="3101cc") ] checksum = GenericCRC.from_standard_checksum("CRC16 CC1101") for mb_builder in [mb_m_frame, mb_c_frame, mb_r_frame, mb_a_frame]: mb_builder.add_label(FieldType.Function.PREAMBLE, 32) mb_builder.add_label(FieldType.Function.SYNC, 32) mb_builder.add_label(FieldType.Function.LENGTH, 8) mb_builder.add_label(FieldType.Function.SEQUENCE_NUMBER, 8) mb_builder.add_label(FieldType.Function.TYPE, 16) mb_builder.add_label(FieldType.Function.SRC_ADDRESS, 24) mb_builder.add_label(FieldType.Function.DST_ADDRESS, 24) if mb_builder.name == "mframe": mb_builder.add_label(FieldType.Function.DATA, 16, name="command") elif mb_builder.name == "cframe": mb_builder.add_label(FieldType.Function.DATA, 16 * 4, name="command+challenge+magic") elif mb_builder.name == "rframe": mb_builder.add_label(FieldType.Function.DATA, 32 * 4, name="cipher") elif mb_builder.name == "aframe": mb_builder.add_label(FieldType.Function.DATA, 10 * 4, name="command + auth") mb_builder.add_checksum_label(16, checksum) message_types = [ mb_m_frame.message_type, mb_c_frame.message_type, mb_r_frame.message_type, mb_a_frame.message_type ] preamble = "0xaaaaaaaa" sync = "0xe9cae9ca" initial_sequence_number = 36 pg = ProtocolGenerator( message_types, participants, preambles_by_mt={mt: preamble for mt in message_types}, syncs_by_mt={mt: sync for mt in message_types}, sequence_numbers={ mt: initial_sequence_number for mt in message_types }, message_type_codes={ mb_m_frame.message_type: 42560, mb_c_frame.message_type: 40962, mb_r_frame.message_type: 40963, mb_a_frame.message_type: 32770 }) for i in range(num_messages): mt = pg.message_types[i % 4] data_length = mt.get_first_label_with_type( FieldType.Function.DATA).length data = "".join( random.choice(["0", "1"]) for _ in range(data_length)) pg.generate_message(mt, data, source=pg.participants[i % 2], destination=pg.participants[(i + 1) % 2]) if save_protocol: cls.save_protocol("homematic", pg) cls.clear_message_types(pg.messages) return pg.protocol
class ChecksumWidgetController(QWidget): SPECIAL_CRCS = OrderedDict([ ("CC1101", GenericCRC(polynomial="16_standard", start_value=True)), ]) class RangeTableModel(QAbstractTableModel): header_labels = ["Start", "End"] def __init__(self, checksum_label: ChecksumLabel, message: Message, proto_view: int, parent=None): """ :param message: :type field_types: list of FieldType :param parent: """ super().__init__(parent) self.checksum_label = checksum_label self.message = message self.proto_view = proto_view self.update() def update(self): self.beginResetModel() self.endResetModel() def columnCount(self, parent: QModelIndex = None, *args, **kwargs): return len(self.header_labels) def rowCount(self, parent: QModelIndex = None, *args, **kwargs): return len(self.checksum_label.data_ranges) def headerData(self, section, orientation, role=Qt.DisplayRole): if role == Qt.DisplayRole and orientation == Qt.Horizontal: return self.header_labels[section] return super().headerData(section, orientation, role) def data(self, index: QModelIndex, role=Qt.DisplayRole): if not index.isValid(): return None i, j = index.row(), index.column() if role == Qt.DisplayRole: data_range = self.checksum_label.data_ranges[i] if j == 0: return self.message.convert_index( data_range[0], 0, self.proto_view, True)[0] + 1 elif j == 1: return self.message.convert_index(data_range[1], 0, self.proto_view, True)[0] return None def setData(self, index: QModelIndex, value, role: int = ...): try: int_val = int(value) except ValueError: return False i, j = index.row(), index.column() if i > len(self.checksum_label.data_ranges): return False data_range = self.checksum_label.data_ranges[i] if j == 0: converted_index = self.message.convert_index( int_val - 1, self.proto_view, 0, True)[0] if converted_index < data_range[1]: data_range[0] = converted_index elif j == 1: converted_index = self.message.convert_index( int_val, self.proto_view, 0, True)[0] if converted_index > data_range[0]: data_range[1] = converted_index return True def flags(self, index): if not index.isValid(): return Qt.NoItemFlags try: _ = self.checksum_label.data_ranges[index.row()] except IndexError: return Qt.NoItemFlags return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable def __init__(self, checksum_label: ChecksumLabel, message: Message, proto_view: int, parent=None): super().__init__(parent) self.ui = Ui_ChecksumOptions() self.ui.setupUi(self) self.checksum_label = checksum_label self.data_range_table_model = self.RangeTableModel(checksum_label, message, proto_view, parent=self) self.ui.tableViewDataRanges.setItemDelegateForColumn( 0, SpinBoxDelegate(1, 999999, self)) self.ui.tableViewDataRanges.setItemDelegateForColumn( 1, SpinBoxDelegate(1, 999999, self)) self.ui.tableViewDataRanges.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.ui.tableViewDataRanges.setModel(self.data_range_table_model) self.ui.tableViewDataRanges.setEditTriggers( QAbstractItemView.AllEditTriggers) self.display_crc_data_ranges_in_table() self.ui.comboBoxCRCFunction.addItems( [crc_name for crc_name in GenericCRC.DEFAULT_POLYNOMIALS]) self.ui.comboBoxCRCFunction.addItems( [special_crc_name for special_crc_name in self.SPECIAL_CRCS]) self.ui.lineEditCRCPolynomial.setValidator( QRegExpValidator(QRegExp("[0-9,a-f]*"))) self.ui.comboBoxCategory.clear() for _, member in self.checksum_label.Category.__members__.items(): self.ui.comboBoxCategory.addItem(member.value) self.set_ui_for_category() self.setFocus() self.create_connects() @property def proto_view(self): return self.data_range_table_model.proto_view @proto_view.setter def proto_view(self, value): if value != self.data_range_table_model.proto_view: self.data_range_table_model.proto_view = value self.data_range_table_model.update() def create_connects(self): self.ui.comboBoxCRCFunction.currentIndexChanged.connect( self.on_combobox_crc_function_current_index_changed) self.ui.btnAddRange.clicked.connect(self.on_btn_add_range_clicked) self.ui.btnRemoveRange.clicked.connect( self.on_btn_remove_range_clicked) self.ui.lineEditCRCPolynomial.editingFinished.connect( self.on_line_edit_crc_polynomial_editing_finished) self.ui.lineEditStartValue.editingFinished.connect( self.on_line_edit_start_value_editing_finished) self.ui.lineEditFinalXOR.editingFinished.connect( self.on_line_edit_final_xor_editing_finished) self.ui.comboBoxCategory.currentIndexChanged.connect( self.on_combobox_category_current_index_changed) self.ui.radioButtonWSPAuto.clicked.connect( self.on_radio_button_wsp_auto_clicked) self.ui.radioButtonWSPChecksum4.clicked.connect( self.on_radio_button_wsp_checksum4_clicked) self.ui.radioButtonWSPChecksum8.clicked.connect( self.on_radio_button_wsp_checksum8_clicked) self.ui.radioButtonWSPCRC8.clicked.connect( self.on_radio_button_wsp_crc8_clicked) def set_checksum_ui_elements(self): if self.checksum_label.is_generic_crc: self.ui.lineEditCRCPolynomial.setText( self.checksum_label.checksum.polynomial_as_hex_str) self.ui.lineEditStartValue.setText( util.bit2hex(self.checksum_label.checksum.start_value)) self.ui.lineEditFinalXOR.setText( util.bit2hex(self.checksum_label.checksum.final_xor)) self.__ensure_same_length() self.__set_crc_info_label() elif self.checksum_label.category == self.checksum_label.Category.wsp: if self.checksum_label.checksum.mode == WSPChecksum.ChecksumMode.auto: self.ui.radioButtonWSPAuto.setChecked(True) elif self.checksum_label.checksum.mode == WSPChecksum.ChecksumMode.checksum4: self.ui.radioButtonWSPChecksum4.setChecked(True) elif self.checksum_label.checksum.mode == WSPChecksum.ChecksumMode.checksum8: self.ui.radioButtonWSPChecksum8.setChecked(True) elif self.checksum_label.checksum.mode == WSPChecksum.ChecksumMode.crc8: self.ui.radioButtonWSPCRC8.setChecked(True) def set_ui_for_category(self): self.ui.comboBoxCategory.setCurrentText( self.checksum_label.category.value) if self.checksum_label.category == self.checksum_label.Category.generic: self.ui.stackedWidget.setCurrentWidget(self.ui.page_crc) elif self.checksum_label.category == self.checksum_label.Category.wsp: self.ui.stackedWidget.setCurrentWidget(self.ui.page_wsp) else: raise ValueError("Unknown category") self.set_checksum_ui_elements() def display_crc_data_ranges_in_table(self): self.data_range_table_model.update() def __set_crc_info_label(self): crc = self.checksum_label.checksum # type: GenericCRC self.ui.label_crc_info.setText("<b>CRC Summary:</b><ul>" "<li>Polynomial = {}<>" "<li>Length of checksum = {} bit</li>" "<li>start value length = {} bit</li>" "<li>final XOR length = {} bit</li>" "</ul>".format(crc.polynomial_to_html, crc.poly_order - 1, len(crc.start_value), len(crc.final_xor))) def __ensure_same_length(self): for dependant_line_edit in [ self.ui.lineEditStartValue, self.ui.lineEditFinalXOR ]: # type: QLineEdit if len(self.ui.lineEditCRCPolynomial.text()) < len( dependant_line_edit.text()): dependant_line_edit.setText( dependant_line_edit.text() [:len(self.ui.lineEditCRCPolynomial.text())]) dependant_line_edit.editingFinished.emit() elif len(self.ui.lineEditCRCPolynomial.text()) > len( dependant_line_edit.text()): # pad zeros at front dependant_line_edit.setText( "0" * (len(self.ui.lineEditCRCPolynomial.text()) - len(dependant_line_edit.text())) + dependant_line_edit.text()) dependant_line_edit.editingFinished.emit() @pyqtSlot() def on_btn_add_range_clicked(self): self.checksum_label.data_ranges.append([0, self.checksum_label.start]) self.data_range_table_model.update() @pyqtSlot() def on_btn_remove_range_clicked(self): if len(self.checksum_label.data_ranges) > 1: self.checksum_label.data_ranges.pop(-1) self.data_range_table_model.update() @pyqtSlot(int) def on_combobox_crc_function_current_index_changed(self, index: int): poly_str = self.ui.comboBoxCRCFunction.itemText(index) if poly_str in GenericCRC.DEFAULT_POLYNOMIALS: self.checksum_label.checksum.polynomial = self.checksum_label.checksum.choose_polynomial( poly_str) self.checksum_label.checksum.start_value = array.array( "B", [0] * (self.checksum_label.checksum.poly_order - 1)) self.checksum_label.checksum.final_xor = array.array( "B", [0] * (self.checksum_label.checksum.poly_order - 1)) elif poly_str in self.SPECIAL_CRCS: self.checksum_label.checksum = copy.deepcopy( self.SPECIAL_CRCS[poly_str]) else: logger.error("Unknown CRC") return self.ui.lineEditCRCPolynomial.setText( self.checksum_label.checksum.polynomial_as_hex_str) self.ui.lineEditStartValue.setText( util.bit2hex(self.checksum_label.checksum.start_value)) self.ui.lineEditFinalXOR.setText( util.bit2hex(self.checksum_label.checksum.final_xor)) self.ui.lineEditCRCPolynomial.editingFinished.emit() @pyqtSlot() def on_line_edit_crc_polynomial_editing_finished(self): self.checksum_label.checksum.set_polynomial_from_hex( self.ui.lineEditCRCPolynomial.text()) self.__ensure_same_length() self.__set_crc_info_label() @pyqtSlot() def on_line_edit_start_value_editing_finished(self): crc = self.checksum_label.checksum start_value = util.hex2bit(self.ui.lineEditStartValue.text()) # pad with zeros at front start_value = array.array( "B", [0] * (crc.poly_order - 1 - len(start_value))) + start_value crc.start_value = start_value[0:crc.poly_order - 1] self.ui.lineEditStartValue.setText(util.bit2hex(crc.start_value)) self.__set_crc_info_label() @pyqtSlot() def on_line_edit_final_xor_editing_finished(self): crc = self.checksum_label.checksum final_xor = util.hex2bit(self.ui.lineEditFinalXOR.text()) final_xor = array.array( "B", [0] * (crc.poly_order - 1 - len(final_xor))) + final_xor crc.final_xor = final_xor[0:crc.poly_order - 1] self.ui.lineEditFinalXOR.setText(util.bit2hex(crc.final_xor)) self.__set_crc_info_label() @pyqtSlot(int) def on_combobox_category_current_index_changed(self, index: int): self.checksum_label.category = self.checksum_label.Category( self.ui.comboBoxCategory.currentText()) self.set_ui_for_category() @pyqtSlot() def on_radio_button_wsp_auto_clicked(self): self.checksum_label.checksum.mode = WSPChecksum.ChecksumMode.auto @pyqtSlot() def on_radio_button_wsp_checksum4_clicked(self): self.checksum_label.checksum.mode = WSPChecksum.ChecksumMode.checksum4 @pyqtSlot() def on_radio_button_wsp_checksum8_clicked(self): self.checksum_label.checksum.mode = WSPChecksum.ChecksumMode.checksum8 @pyqtSlot() def on_radio_button_wsp_crc8_clicked(self): self.checksum_label.checksum.mode = WSPChecksum.ChecksumMode.crc8
def test_different_crcs(self): c = GenericCRC(polynomial="16_standard", start_value=False, final_xor=False, reverse_polynomial=False, reverse_all=False, lsb_first=False, little_endian=False) bitstring_set = [ "101001001010101010101011101111111000000000000111101010011101011", "101001001010101101111010110111101010010110111010", "00000000000000000000000000000000100000000000000000000000000000000001111111111111", "1111111111111111111111111111111110111111111111111111110111111111111111110000000000" "1" ] for j in c.DEFAULT_POLYNOMIALS: c.polynomial = c.choose_polynomial(j) for i in bitstring_set: # Standard crc_new = c.crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_new, crc_old) # Special final xor c.final_xor = c.str2bit("0000111100001111") crc_new = c.crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_new, crc_old) c.final_xor = [False] * 16 # Special start value c.start_value = c.str2bit("1010101010101010") crc_new = c.crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_new, crc_old) c.start_value = [False] * 16 # reverse_polynomial c.reverse_polynomial = True crc_new = c.crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_new, crc_old) c.reverse_polynomial = False # lsb_first c.lsb_first = True crc_new = c.crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_new, crc_old) c.lsb_first = False # little_endian c.little_endian = True crc_new = c.crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_new, crc_old) c.little_endian = False # reverse all c.reverse_all = True crc_new = c.crc(c.str2bit(i)) crc_old = c.reference_crc(c.str2bit(i)) self.assertEqual(crc_new, crc_old) c.reverse_all = False
def find(self): result = list() bitvectors_by_n_gram_length = defaultdict(list) for i, bitvector in enumerate(self.bitvectors): bin_num = int(math.ceil(len(bitvector) / self.n_gram_length)) bitvectors_by_n_gram_length[bin_num].append(i) crc = GenericCRC() for length, message_indices in bitvectors_by_n_gram_length.items(): checksums_for_length = [] for index in message_indices: bits = self.bitvectors[index] data_start, data_stop, crc_start, crc_stop = WSPChecksum.search_for_wsp_checksum( bits) if (data_start, data_stop, crc_start, crc_stop) != (0, 0, 0, 0): checksum_range = ChecksumRange(start=crc_start, length=crc_stop - crc_start, data_range_start=data_start, data_range_end=data_stop, crc=WSPChecksum(), score=1 / len(message_indices), field_type="checksum", message_indices={index}) try: present = next(c for c in checksums_for_length if c == checksum_range) present.message_indices.add(index) except StopIteration: checksums_for_length.append(checksum_range) continue crc_object, data_start, data_stop, crc_start, crc_stop = crc.guess_all( bits, ignore_positions=self.already_labeled_cols) if (crc_object, data_start, data_stop, crc_start, crc_stop) != (0, 0, 0, 0, 0): checksum_range = ChecksumRange(start=crc_start, length=crc_stop - crc_start, data_range_start=data_start, data_range_end=data_stop, crc=copy.copy(crc_object), score=1 / len(message_indices), field_type="checksum", message_indices={index}) try: present = next(rng for rng in checksums_for_length if rng == checksum_range) present.message_indices.add(index) continue except StopIteration: pass checksums_for_length.append(checksum_range) matching = awre_util.check_crc_for_messages( message_indices, self.bitvectors, data_start, data_stop, crc_start, crc_stop, *crc_object.get_parameters()) checksum_range.message_indices.update(matching) # Score ranges for rng in checksums_for_length: rng.score = len(rng.message_indices) / len(message_indices) try: result.append(max(checksums_for_length, key=lambda x: x.score)) except ValueError: pass # no checksums found for this length self._debug("Found Checksums", result) try: max_scored = max(filter( lambda x: len(x.message_indices) >= 2 and x.score >= self. minimum_score, result), key=lambda x: x.score) except ValueError: return [] result = list(filter(lambda x: x.crc == max_scored.crc, result)) self._debug("Filtered Checksums", result) return result
def apply_data_whitening(self, decoding, inpt): len_sync = len(self.data_whitening_sync) len_polynomial = len(self.data_whitening_polynomial) inpt_from = 0 inpt_to = len(inpt) # Crop last bit, if duplicate if decoding and inpt_to > 1: if inpt[-1] == inpt[-2]: inpt_to -= 1 # # Crop last bit, if len not multiple of 8 # if decoding and inpt_to % 8 != 0: # inpt_to -= (8 - (inpt_to % 8)) % 8 # inpt empty, polynomial or syncbytes are zero! (Shouldn't happen) if inpt_to < 1 or len_polynomial < 1 or len_sync < 1: return inpt[inpt_from:inpt_to], 0, self.ErrorState.MISC # Misc Error # Search for whitening start position (after sync bytes) whitening_start_pos = inpt_from i = inpt_from while i < (inpt_to - len_sync): equalbits = 0 for j in range(0, len_sync): if inpt[i + j] == self.data_whitening_sync[j]: equalbits += 1 else: continue if len_sync == equalbits: whitening_start_pos = i + j + 1 break else: i += 1 # Sync not found if decoding and whitening_start_pos == inpt_from: return inpt[inpt_from:inpt_to], 0, self.ErrorState.SYNC_NOT_FOUND # Prepare keystream self.lfsr_state = array.array("B", []) keystream = self.lfsr(0) for i in range(whitening_start_pos, inpt_to, 8): keystream.extend(self.lfsr(8)) # If data whitening polynomial is wrong, keystream can be less than needed. Check and exit. if len(keystream) < inpt_to - whitening_start_pos: return inpt[inpt_from:inpt_to], 0, self.ErrorState.MISC # Error 31338 # Overwrite crc16 in encoding case if not decoding and self.cc1101_overwrite_crc: # Remove additional bits offset = inpt_to % 8 data_end = inpt_to - 16 - offset c = GenericCRC(polynomial="16_standard", start_value=True) crc = c.crc(inpt[whitening_start_pos:data_end]) for i in range(0, 16): inpt[data_end + i] = crc[i] # Apply keystream (xor) for i in range(whitening_start_pos, inpt_to): inpt[i] ^= keystream[i - whitening_start_pos] # Duplicate last bit when encoding if not decoding: inpt += array.array("B", [inpt[-1]]) inpt_to += 1 return inpt[inpt_from:inpt_to], 0, self.ErrorState.SUCCESS
def crc8(cls, bits: array.array): return array.array( "B", GenericCRC(polynomial=cls.CRC_8_POLYNOMIAL).crc(bits))