in reply to Using Digest::CRC to calculate DNP3 checksums
I thought I'd challenge myself to see if I could figure this out. As I started looking into CRC algorithms, I quickly found myself in over my head. But I think that I was actually able to figure it out.
Looking at the source code of Digest::CRC, I took a guess that the value for 'const' should be 0 instead of '0xea82'. That got me closer for ASCII input. Combining that with the suggestion from the Anonymous Monk got me closer for hex input. In both cases what I mean by getting closer is that I got the right characters, but in the wrong order. Basically, I needed to reverse the output by bits. For example, with your sample header, I was getting 'c52' instead of '520c'. The two bits here are '0c' and '52'. Reversing the two bits of 'c52' (or '0c52') produces '520c'.
In searching for references for CRC-DNP checksums, I found an online checksum calculator (found here). I used it to 'verify' the output of the following code.
use strict; use warnings; use feature 'say'; use Digest::CRC; sub Left_Pad { my $value = shift; if (length($value) % 2) {$value = '0'.$value;} return $value; } sub Bitwise_Reverse { my $value = shift; $value = Left_Pad($value); my $offset = length($value); my $reversed; while ($offset > 0) { $offset -= 2; my $string = substr($value,$offset,2); $reversed .= $string; } return $reversed; } sub CRC_DNP_ASCII { my $value = shift; my $ctx = Digest::CRC->new(width=>16, init=>0x0, xorout=>0xffff, r +efin=>1, refout=>1, poly=>0x3d65, cont=>0); $ctx->add($value); my $digest = $ctx->hexdigest; my $crc = Bitwise_Reverse($digest); return $crc; } sub CRC_DNP_HEX { my $value = shift; my $ctx = Digest::CRC->new(width=>16, init=>0x0, xorout=>0xffff, r +efin=>1, refout=>1, poly=>0x3d65, cont=>0); my $offset = 0; while ($offset < length($value)) { my $string = substr($value,$offset,2); $ctx->add(chr(hex($string))); $offset += 2; } my $digest = $ctx->hexdigest; my $crc = Bitwise_Reverse($digest); return $crc; } my @data_list = ('056405F201000000','56405F201000000'); foreach my $data (@data_list) { say "Calculating CRC-DNP checksum for '$data':"; my $ascii = CRC_DNP_ASCII($data); say " ASCII input: $ascii"; my $hex = CRC_DNP_HEX($data); say " Hex input: $hex\n"; }
That produced the following output:
Calculating CRC-DNP checksum for '056405F201000000': ASCII input: 99fc Hex input: 520c Calculating CRC-DNP checksum for '56405F201000000': ASCII input: 0751 Hex input: 11e3
I've tried a few other examples to compare the output of my code versus the online calculator and I'm getting the same values for the CRC-DNP checksums.
Perhaps more knowledgeable monks might be able to identify issues with my code, but I think it should help you get further along with what you're trying to do.
UPDATE: Since the OP was cross posted, I cross posted this response (see here) as well to the same site.
|
|---|