How to apply xmodem CRC on generating and checking

1.1k views Asked by At

I've iterated of lots of articles - for some reason I couldn't find any which describes what must be a simple procedure: How to combine a calculated CRC with the original message, so that calculating CRC again results in 0 (=checks out as correct)? I did find several examples with 'longhand' calculations (only 2 or 3 bit CRCs), but no example which uses a library function such as [crcmod][1] (Python library).

Here's a simple program I wrote to check it out:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#

import crcmod

def test_cycle():
    test_str = b"123456789"
    orig_msg = test_str
    print("Original message: {:s}".format(orig_msg.hex()))

    #~ crc_calc = crcmod.predefined.Crc('xmodem')
    crc_calc = crcmod.Crc(
                0x11021,
                rev = False,
                initCrc = 0x0000,
                xorOut = 0x0000)

    crc_calc.update(orig_msg)
    print("CRC: {:04x}".format(crc_calc.crcValue))

    lo = crc_calc.crcValue & 0xff
    hi = crc_calc.crcValue >> 8
    new_msg = test_str + bytes((hi, lo))
    print("Crc appended: {:s}".format(new_msg.hex()))

    crc_calc.update(new_msg)
    print("CRC: {:04x}".format(crc_calc.crcValue))


def main(args):
    test_cycle()
    return 0

if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))

There are some commented lines, from experiments with different byte ordering. The results from the program are:

Original message: 313233343536373839
CRC: 31c3
Crc appended: 31323334353637383931c3
CRC: 00ef

The first CRC (31C3) seems to check out with correspond with the expected values for xmodem-CRC. I tried, in many ways, to combine the obtained CRC with the original string, but never obtain '0'. Am I missing something here?

2

There are 2 answers

0
Adrian W On BEST ANSWER

crc_calc.update(new_msg) adds the entire content of new_msg to the CRC. Since crc_calc already holds the result of 313233343536373839, you are effectively calculating the CRC of 31323334353637383931323334353637383931c3 and this is indeed 00ef.

To add only the two bytes to the calculation, use

crc_calc.update(bytes((hi, lo)))

Alternatively, use a new instance of crcmod.Crc(), or reset the crcValue of the existing instance before doing a new calculation

crc_calc.crcValue = 0
crc_calc.update(new_msg)

Both will give you a result of 0

1
Mark Adler On

Some background for those arriving at this question, wondering about appending CRCs.

If a CRC is properly appended to the message that it is the CRC of, and there are no errors introduced in the message or CRC, the the CRC of that whole thing will be a constant dependent only on the definition of that CRC.

To properly append the CRC requires care in the bit ordering. For the generic CRC definition afforded by crcmod, if rev is false, then the bits must be appended most significant first. If rev is true, then the bits must be appended least significant first. For byte-oriented messages, this means first off that the width of the CRC has to be a multiple of eight bits (which by the way is all that crcmod permits), and that the CRC be appended in big-endian order or little-endian order respectively.

The resulting constant is not always zero, depending on the definition of the CRC. It is zero if the xorOut value for the CRC is zero. Otherwise the constant is the CRC of n zero bits, where n is the width of the CRC, and where the initial CRC value provided is zero (not initCrc). As an example, for the standard CRC-32, the CRC-32 of a message with its CRC-32 appended in little-endian order is always 0x2144df1c.

For this particular question, the CRC is appended in big-endian order, so 31 c3, and CRC of the resulting message + CRC is then zero.