""" Classes to calculate CRCs (Cyclic Redundancy Check). License:: MIT License Copyright (c) 2015-2022 by Martin Scharrer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from crccheck.base import CrccheckBase, reflectbitorder, REFLECT_BIT_ORDER_TABLE, CrccheckError class CrcBase(CrccheckBase): """Abstract base class for all Cyclic Redundancy Checks (CRC) checksums""" _names = () _width = 0 _poly = 0x00 _initvalue = 0x00 _reflect_input = False _reflect_output = False _xor_output = 0x00 _check_result = None _check_data = bytearray(b"123456789") _residue = None @classmethod def poly(cls): """Getter for polynominal value.""" return cls._poly @classmethod def reflect_input(cls): """Getter for reflect_input.""" return cls._reflect_input @classmethod def reflect_output(cls): """Getter for reflect_output.""" return cls._reflect_output @classmethod def xor_output(cls): """Getter for xor_output value.""" return cls._xor_output @classmethod def residue(cls): """Getter for residue value.""" return cls._residue def process(self, data): """ Process given data. Args: data (bytes, bytearray or list of ints [0-255]): input data to process. Returns: self """ crc = self._value highbit = 1 << (self._width - 1) mask = (1 << self._width) - 1 poly = self._poly shift = self._width - 8 diff8 = -shift if diff8 > 0: # enlarge temporary to fit 8-bit mask = 0xFF crc <<= diff8 shift = 0 highbit = 0x80 poly = self._poly << diff8 reflect = self._reflect_input for byte in data: if reflect: byte = REFLECT_BIT_ORDER_TABLE[byte] crc ^= (byte << shift) for i in range(0, 8): if crc & highbit: crc = (crc << 1) ^ poly else: crc = (crc << 1) crc &= mask if diff8 > 0: crc >>= diff8 self._value = crc return self def final(self): """ Return final CRC value. Return: int: final CRC value """ crc = self._value if self._reflect_output: crc = reflectbitorder(self._width, crc) crc ^= self._xor_output return crc def __eq__(self, other): # noinspection PyProtectedMember return self._width == other.width() and \ self._poly == other.poly() and \ self._initvalue == other.initvalue() and \ self._reflect_input == other.reflect_input() and \ self._reflect_output == other.reflect_output() and \ self._xor_output == other.xor_output() def __repr__(self): residue = hex(self._residue) if self._residue is not None else 'None' check_result = hex(self._check_result) if self._check_result is not None else 'None' return ("Crc(width={:d}, poly=0x{:x}, initvalue=0x{:X}, reflect_input={!s:s}, reflect_output={!s:s}, " + "xor_output=0x{:x}, check_result={}, residue={})").format( self._width, self._poly, self._initvalue, self._reflect_input, self._reflect_output, self._xor_output, check_result, residue) def find(classes=None, width=None, poly=None, initvalue=None, reflect_input=None, reflect_output=None, xor_output=None, check_result=None, residue=None): """Find CRC classes which the matching properties. Args: classes (None or list): List of classes to search in. If None the list ALLCRCCLASSES will be used. width (None or int): number of bits of the CRC classes to find poly (None or int): polygon to find initvalue (None or int): initvalue to find reflect_input (None or bool): reflect_input to find reflect_output (None or bool): reflect_output to find xor_output (None or int): xor_output to find check_result (None or int): check_result to find residue (None or int): residue to find Returns: List of CRC classes with the selected properties. Examples: Find all CRC16 classes: $ find(width=16) Find all CRC32 classes with all-1 init value and XOR output: $ find(width=32, initvalue=0xFFFF, xor_output=0xFFFF) """ found = list() if classes is None: classes = ALLCRCCLASSES for cls in classes: if width is not None and width != cls._width: continue if poly is not None and poly != cls._poly: continue if initvalue is not None and initvalue != cls._initvalue: continue if reflect_input is not None and reflect_input != cls._reflect_input: continue if reflect_output is not None and reflect_output != cls._reflect_output: continue if xor_output is not None and xor_output != cls._xor_output: continue if check_result is not None and check_result != cls._check_result: continue if residue is not None and residue != cls._residue: continue found.append(cls) return found def identify(data, crc, width=None, classes=None, one=True): """ Identify the used CRC algorithm which was used to calculate the CRC from some data. This function can be used to identify a suitable CRC class if the exact CRC algorithm/parameters are not known, but a CRC value is known from some data. Note that this function can be quite time-consuming on large data, especially if the given width is not known. Args: data (bytes): Data to compare with the `crc`. crc (int): Known CRC of the given `data`. width (int or None): Known bit-width of given `crc`. Providing the width will speed up the identification of the CRC algorithm. classes (iterable or None): Listing of classes to check. If None then ALLCRCCLASSES is used. one (bool): If True then only the first found CRC class is returned, otherwise a list of all suitable CRC classes. Returns: If `one` is True: CRC class which instances produce the given CRC from the given data. If no CRC class could be found `None` is returned. If `one` is False: List of CRC classes which instances produce the given CRC from the given data. The list may be empty. """ if classes is None: classes = ALLCRCCLASSES if width is not None: classes = (cls for cls in classes if cls._width == width) found = [] for cls in classes: if cls().calc(data) == crc: if one: return cls found.append(cls) if one: return None return found class Crc(CrcBase): """ Creates a new general (user-defined) CRC calculator instance. Arguments: width (int): bit-width of CRC. poly (int): polynomial of CRC with the top bit omitted. initvalue (int): initial value of internal running CRC value. Usually either 0 or (1<