ViggoTW
ViggoTW

Reputation: 756

Validating the checksum using binascii.crc32()

I am sending multiple messages from an MCU to my computer and I want to use a 32-bit Cyclic Redundancy Check to verify that the messages are correct. From what I've read, it should be possible to append the CRC remainder to the last message and run that through the CRC function. If the message was error free, it should then return zero (am I right?) I have however been unsuccessful in implementing this using the binascii library function binascii.crc32().

Let's for example say I want to check the message internally from the example given in Python docs (I'm using Python 3.5). How would I go ahead checking if the message is error free (which of course it is in this example)?

crc = binascii.crc32(b"hello")
crc = binascii.crc32(b" world", crc)

check_for_error() # <--- ?

Upvotes: 3

Views: 3382

Answers (1)

PM 2Ring
PM 2Ring

Reputation: 55469

The simple way to do it is to just append the CRC in byte form to the message. Then when you receive the message, you calculate the CRC of all but the last 4 bytes of the message and compare it to appended CRC bytes. Sure, it's a bit more fiddly than what you want to do, but you can apply that strategy to cryptographic hashes like MD5 or the SHA family.

However, to do what you're asking you need to invert the CRC32 by subtracting it from 0xffffffff before converting it to bytes & appending it. The CRC32 is actually an inverse CRC, which prevents a message of all zero bytes from having a zero CRC. On decoding, the message may be valid if the CRC of the data + CRC equals 0xffffffff.

The Python CRC32 docs recommend that you use

crc32(data) & 0xffffffff

rather than

crc32(data)

to ensure that you get the same numeric value across all Python versions and platforms.

Here's a quick Python 3 demo.

import binascii

maxcrc = 0xffffffff

def inverse_crc(data):
    crc = binascii.crc32(data) & maxcrc
    invcrc = maxcrc - crc
    return invcrc.to_bytes(4, 'little')

def check_crc(data):
    return binascii.crc32(data) & maxcrc == maxcrc    

#Test

data = b"Hello, world"
newdata = data + inverse_crc(data)
print(check_crc(newdata))
newdata = b'0x00' + newdata
print(check_crc(newdata))

output

True
False

Note that you can get false positives: a corrupt message may have a correct CRC. If you need a higher level of protection you should use a cryptographic hash. That's still not perfect, but the odds of a false positive with such large hashes are extremely low. Of course, calculating a MD5 or SHA hash is much slower than calculating a CRC32.

Upvotes: 5

Related Questions